Compare commits
	
		
			25 Commits
		
	
	
		
			v7.5.0
			...
			feature/ma
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					8192da790f | ||
| 
						 | 
					33e7271b85 | ||
| 
						 | 
					d72e5e70f6 | ||
| 
						 | 
					e2c5f751bd | ||
| 
						 | 
					351b86e266 | ||
| 
						 | 
					d0cbff4f4e | ||
| 
						 | 
					cbfc9b9f94 | ||
| 
						 | 
					ca816d801f | ||
| 
						 | 
					2f354d31eb | ||
| 
						 | 
					2c6c1edd37 | ||
| 
						 | 
					9799e7e84b | ||
| 
						 | 
					81be970679 | ||
| 
						 | 
					52221906f6 | ||
| 
						 | 
					3e786fe23a | ||
| 
						 | 
					de24aac7d5 | ||
| 
						 | 
					cd4b0ccf6f | ||
| 
						 | 
					4e1888ac19 | ||
| 
						 | 
					237ede56aa | ||
| 
						 | 
					ba3b1c1a0f | ||
| 
						 | 
					c60c606e0f | ||
| 
						 | 
					5897de6bd9 | ||
| 
						 | 
					cca304fc18 | ||
| 
						 | 
					432624df0d | ||
| 
						 | 
					9c047fac72 | ||
| 
						 | 
					615df9cef0 | 
							
								
								
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -1,3 +1,7 @@
 | 
			
		||||
build
 | 
			
		||||
*.pyc
 | 
			
		||||
venv
 | 
			
		||||
ixsnake/ixsnake/.certs/
 | 
			
		||||
site/
 | 
			
		||||
ws/.certs/
 | 
			
		||||
ws/.srl
 | 
			
		||||
 
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
7.5.0
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
FROM alpine:edge as build
 | 
			
		||||
FROM alpine:3.11 as build
 | 
			
		||||
 | 
			
		||||
RUN apk add --no-cache gcc g++ musl-dev linux-headers cmake openssl-dev 
 | 
			
		||||
RUN apk add --no-cache make
 | 
			
		||||
@@ -16,7 +16,7 @@ WORKDIR /opt
 | 
			
		||||
USER app
 | 
			
		||||
RUN [ "make", "ws_install" ]
 | 
			
		||||
 | 
			
		||||
FROM alpine:edge as runtime
 | 
			
		||||
FROM alpine:3.11 as runtime
 | 
			
		||||
 | 
			
		||||
RUN apk add --no-cache libstdc++
 | 
			
		||||
RUN apk add --no-cache strace
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,72 @@
 | 
			
		||||
# Changelog
 | 
			
		||||
All notable changes to this project will be documented in this file.
 | 
			
		||||
All changes to this project will be documented in this file.
 | 
			
		||||
 | 
			
		||||
## [7.6.4] - 2019-12-22
 | 
			
		||||
 | 
			
		||||
(client) error handling, quote url in error case when failing to parse one
 | 
			
		||||
(ws) ws_cobra_publish: register callbacks before connecting
 | 
			
		||||
(doc) mention mbedtls in supported ssl server backend
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## [7.6.3] - 2019-12-20
 | 
			
		||||
 | 
			
		||||
(tls) add a simple description of the TLS configuration routine for debugging
 | 
			
		||||
 | 
			
		||||
## [7.6.2] - 2019-12-20
 | 
			
		||||
 | 
			
		||||
(mbedtls) correct support for using own certificate and private key
 | 
			
		||||
 | 
			
		||||
## [7.6.1] - 2019-12-20
 | 
			
		||||
 | 
			
		||||
(ws commands) in websocket proxy, disable automatic reconnections + in Dockerfile, use alpine 3.11
 | 
			
		||||
 | 
			
		||||
## [7.6.0] - 2019-12-19
 | 
			
		||||
 | 
			
		||||
(cobra) Add TLS options to all cobra commands and classes. Add example to the doc.
 | 
			
		||||
 | 
			
		||||
## [7.5.8] - 2019-12-18
 | 
			
		||||
 | 
			
		||||
(cobra-to-sentry) capture application version from device field
 | 
			
		||||
 | 
			
		||||
## [7.5.7] - 2019-12-18
 | 
			
		||||
 | 
			
		||||
(tls) Experimental TLS server support with mbedtls (windows) + process cert tlsoption (client + server)
 | 
			
		||||
 | 
			
		||||
## [7.5.6] - 2019-12-18
 | 
			
		||||
 | 
			
		||||
(tls servers) Make it clear that apple ssl and mbedtls backends do not support SSL in server mode
 | 
			
		||||
 | 
			
		||||
## [7.5.5] - 2019-12-17
 | 
			
		||||
 | 
			
		||||
(tls options client) TLSOptions struct _validated member should be initialized to false
 | 
			
		||||
 | 
			
		||||
## [7.5.4] - 2019-12-16
 | 
			
		||||
 | 
			
		||||
(websocket client) improve the error message when connecting to a non websocket server
 | 
			
		||||
 | 
			
		||||
Before:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
Connection error: Got bad status connecting to example.com:443, status: 200, HTTP Status line: HTTP/1.1 200 OK
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
After:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
Connection error: Expecting status 101 (Switching Protocol), got 200 status connecting to example.com:443, HTTP Status line: HTTP/1.1 200 OK
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## [7.5.3] - 2019-12-12
 | 
			
		||||
 | 
			
		||||
(server) attempt at fixing #131 by using blocking writes in server mode
 | 
			
		||||
 | 
			
		||||
## [7.5.2] - 2019-12-11
 | 
			
		||||
 | 
			
		||||
(ws) cobra to sentry - created events with sentry tags based on tags present in the cobra messages
 | 
			
		||||
 | 
			
		||||
## [7.5.1] - 2019-12-06
 | 
			
		||||
 | 
			
		||||
(mac) convert SSL errors to utf8
 | 
			
		||||
 | 
			
		||||
## [7.5.0] - 2019-12-05
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										125
									
								
								docs/ws.md
									
									
									
									
									
								
							
							
						
						
									
										125
									
								
								docs/ws.md
									
									
									
									
									
								
							@@ -243,6 +243,127 @@ Options:
 | 
			
		||||
  --transfer-timeout INT      Transfer timeout
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Cobra Client
 | 
			
		||||
## Cobra client and server
 | 
			
		||||
 | 
			
		||||
[cobra](https://github.com/machinezone/cobra) is a real time messenging server. ws has a sub-command to interact with cobra.
 | 
			
		||||
[cobra](https://github.com/machinezone/cobra) is a real time messenging server. ws has several sub-command to interact with cobra. There is also a minimal cobra compatible server named snake available.
 | 
			
		||||
 | 
			
		||||
Below are examples on running a snake server and clients with TLS enabled (the server only works with the OpenSSL and the Mbed TLS backend for now).
 | 
			
		||||
 | 
			
		||||
First, generate certificates.
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
$ cd /path/to/IXWebSocket
 | 
			
		||||
$ cd ixsnake/ixsnake
 | 
			
		||||
$ bash ../../ws/generate_certs.sh
 | 
			
		||||
Generating RSA private key, 2048 bit long modulus
 | 
			
		||||
.....+++
 | 
			
		||||
.................+++
 | 
			
		||||
e is 65537 (0x10001)
 | 
			
		||||
generated ./.certs/trusted-ca-key.pem
 | 
			
		||||
generated ./.certs/trusted-ca-crt.pem
 | 
			
		||||
Generating RSA private key, 2048 bit long modulus
 | 
			
		||||
..+++
 | 
			
		||||
.......................................+++
 | 
			
		||||
e is 65537 (0x10001)
 | 
			
		||||
generated ./.certs/trusted-server-key.pem
 | 
			
		||||
Signature ok
 | 
			
		||||
subject=/O=machinezone/O=IXWebSocket/CN=trusted-server
 | 
			
		||||
Getting CA Private Key
 | 
			
		||||
generated ./.certs/trusted-server-crt.pem
 | 
			
		||||
Generating RSA private key, 2048 bit long modulus
 | 
			
		||||
...................................+++
 | 
			
		||||
..................................................+++
 | 
			
		||||
e is 65537 (0x10001)
 | 
			
		||||
generated ./.certs/trusted-client-key.pem
 | 
			
		||||
Signature ok
 | 
			
		||||
subject=/O=machinezone/O=IXWebSocket/CN=trusted-client
 | 
			
		||||
Getting CA Private Key
 | 
			
		||||
generated ./.certs/trusted-client-crt.pem
 | 
			
		||||
Generating RSA private key, 2048 bit long modulus
 | 
			
		||||
..............+++
 | 
			
		||||
.......................................+++
 | 
			
		||||
e is 65537 (0x10001)
 | 
			
		||||
generated ./.certs/untrusted-ca-key.pem
 | 
			
		||||
generated ./.certs/untrusted-ca-crt.pem
 | 
			
		||||
Generating RSA private key, 2048 bit long modulus
 | 
			
		||||
..........+++
 | 
			
		||||
................................................+++
 | 
			
		||||
e is 65537 (0x10001)
 | 
			
		||||
generated ./.certs/untrusted-client-key.pem
 | 
			
		||||
Signature ok
 | 
			
		||||
subject=/O=machinezone/O=IXWebSocket/CN=untrusted-client
 | 
			
		||||
Getting CA Private Key
 | 
			
		||||
generated ./.certs/untrusted-client-crt.pem
 | 
			
		||||
Generating RSA private key, 2048 bit long modulus
 | 
			
		||||
.....................................................................................+++
 | 
			
		||||
...........+++
 | 
			
		||||
e is 65537 (0x10001)
 | 
			
		||||
generated ./.certs/selfsigned-client-key.pem
 | 
			
		||||
Signature ok
 | 
			
		||||
subject=/O=machinezone/O=IXWebSocket/CN=selfsigned-client
 | 
			
		||||
Getting Private key
 | 
			
		||||
generated ./.certs/selfsigned-client-crt.pem
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Now run the snake server.
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
$ export certs=.certs
 | 
			
		||||
$ ws snake --tls --port 8765 --cert-file ${certs}/trusted-server-crt.pem --key-file ${certs}/trusted-server-key.pem --ca-file ${certs}/trusted-ca-crt.pem
 | 
			
		||||
{
 | 
			
		||||
  "apps": {
 | 
			
		||||
    "FC2F10139A2BAc53BB72D9db967b024f": {
 | 
			
		||||
      "roles": {
 | 
			
		||||
        "_sub": {
 | 
			
		||||
          "secret": "66B1dA3ED5fA074EB5AE84Dd8CE3b5ba"
 | 
			
		||||
        },
 | 
			
		||||
        "_pub": {
 | 
			
		||||
          "secret": "1c04DB8fFe76A4EeFE3E318C72d771db"
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
redis host: 127.0.0.1
 | 
			
		||||
redis password:
 | 
			
		||||
redis port: 6379
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
As a new connection comes in, such output should be printed
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
[2019-12-19 20:27:19.724] [info] New connection
 | 
			
		||||
id: 0
 | 
			
		||||
Uri: /v2?appkey=_health
 | 
			
		||||
Headers:
 | 
			
		||||
Connection: Upgrade
 | 
			
		||||
Host: 127.0.0.1:8765
 | 
			
		||||
Sec-WebSocket-Extensions: permessage-deflate; server_max_window_bits=15; client_max_window_bits=15
 | 
			
		||||
Sec-WebSocket-Key: d747B0fE61Db73f7Eh47c0==
 | 
			
		||||
Sec-WebSocket-Protocol: json
 | 
			
		||||
Sec-WebSocket-Version: 13
 | 
			
		||||
Upgrade: websocket
 | 
			
		||||
User-Agent: ixwebsocket/7.5.8 macos ssl/OpenSSL OpenSSL 1.0.2q  20 Nov 2018 zlib 1.2.11
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
To connect and publish a message, do:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
$ export certs=.certs
 | 
			
		||||
$ cd /path/to/ws/folder
 | 
			
		||||
$ ls cobraMetricsSample.json
 | 
			
		||||
cobraMetricsSample.json
 | 
			
		||||
$ ws cobra_publish --endpoint wss://127.0.0.1:8765 --appkey FC2F10139A2BAc53BB72D9db967b024f --rolename _pub --rolesecret 1c04DB8fFe76A4EeFE3E318C72d771db --channel foo --cert-file ${certs}/trusted-client-crt.pem --key-file ${certs}/trusted-client-key.pem --ca-file ${certs}/trusted-ca-crt.pem cobraMetricsSample.json
 | 
			
		||||
[2019-12-19 20:46:42.656] [info] Publisher connected
 | 
			
		||||
[2019-12-19 20:46:42.657] [info] Connection: Upgrade
 | 
			
		||||
[2019-12-19 20:46:42.657] [info] Sec-WebSocket-Accept: rs99IFThoBrhSg+k8G4ixH9yaq4=
 | 
			
		||||
[2019-12-19 20:46:42.657] [info] Sec-WebSocket-Extensions: permessage-deflate; server_max_window_bits=15; client_max_window_bits=15
 | 
			
		||||
[2019-12-19 20:46:42.657] [info] Server: ixwebsocket/7.5.8 macos ssl/OpenSSL OpenSSL 1.0.2q  20 Nov 2018 zlib 1.2.11
 | 
			
		||||
[2019-12-19 20:46:42.657] [info] Upgrade: websocket
 | 
			
		||||
[2019-12-19 20:46:42.658] [info] Publisher authenticated
 | 
			
		||||
[2019-12-19 20:46:42.658] [info] Published msg 3
 | 
			
		||||
[2019-12-19 20:46:42.659] [info] Published message id 3 acked
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
To use OpenSSL on macOS, compile with `make ws_openssl`. First you will have to install OpenSSL libraries, which can be done with Homebrew. Use `make ws_mbedtls` accordingly to use MbedTLS.
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,7 @@
 | 
			
		||||
#include "IXCobraConnection.h"
 | 
			
		||||
#include <ixcrypto/IXHMac.h>
 | 
			
		||||
#include <ixwebsocket/IXWebSocket.h>
 | 
			
		||||
#include <ixwebsocket/IXSocketTLSOptions.h>
 | 
			
		||||
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <stdexcept>
 | 
			
		||||
@@ -244,7 +245,8 @@ namespace ix
 | 
			
		||||
                                    const std::string& endpoint,
 | 
			
		||||
                                    const std::string& rolename,
 | 
			
		||||
                                    const std::string& rolesecret,
 | 
			
		||||
                                    const WebSocketPerMessageDeflateOptions& webSocketPerMessageDeflateOptions)
 | 
			
		||||
                                    const WebSocketPerMessageDeflateOptions& webSocketPerMessageDeflateOptions,
 | 
			
		||||
                                    const SocketTLSOptions& socketTLSOptions)
 | 
			
		||||
    {
 | 
			
		||||
        _roleName = rolename;
 | 
			
		||||
        _roleSecret = rolesecret;
 | 
			
		||||
@@ -257,6 +259,7 @@ namespace ix
 | 
			
		||||
        std::string url = ss.str();
 | 
			
		||||
        _webSocket->setUrl(url);
 | 
			
		||||
        _webSocket->setPerMessageDeflateOptions(webSocketPerMessageDeflateOptions);
 | 
			
		||||
        _webSocket->setTLSOptions(socketTLSOptions);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //
 | 
			
		||||
 
 | 
			
		||||
@@ -20,6 +20,7 @@
 | 
			
		||||
namespace ix
 | 
			
		||||
{
 | 
			
		||||
    class WebSocket;
 | 
			
		||||
    struct SocketTLSOptions;
 | 
			
		||||
 | 
			
		||||
    enum CobraConnectionEventType
 | 
			
		||||
    {
 | 
			
		||||
@@ -62,7 +63,8 @@ namespace ix
 | 
			
		||||
                       const std::string& endpoint,
 | 
			
		||||
                       const std::string& rolename,
 | 
			
		||||
                       const std::string& rolesecret,
 | 
			
		||||
                       const WebSocketPerMessageDeflateOptions& webSocketPerMessageDeflateOptions);
 | 
			
		||||
                       const WebSocketPerMessageDeflateOptions& webSocketPerMessageDeflateOptions,
 | 
			
		||||
                       const SocketTLSOptions& socketTLSOptions);
 | 
			
		||||
 | 
			
		||||
        /// Set the traffic tracker callback
 | 
			
		||||
        static void setTrafficTrackerCallback(const TrafficTrackerCallback& callback);
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "IXCobraMetricsPublisher.h"
 | 
			
		||||
#include <ixwebsocket/IXSocketTLSOptions.h>
 | 
			
		||||
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <stdexcept>
 | 
			
		||||
@@ -31,14 +32,15 @@ namespace ix
 | 
			
		||||
                                          const std::string& channel,
 | 
			
		||||
                                          const std::string& rolename,
 | 
			
		||||
                                          const std::string& rolesecret,
 | 
			
		||||
                                          bool enablePerMessageDeflate)
 | 
			
		||||
                                          bool enablePerMessageDeflate,
 | 
			
		||||
                                          const SocketTLSOptions& socketTLSOptions)
 | 
			
		||||
    {
 | 
			
		||||
        // Configure the satori connection and start its publish background thread
 | 
			
		||||
        _cobra_metrics_theaded_publisher.start();
 | 
			
		||||
 | 
			
		||||
        _cobra_metrics_theaded_publisher.configure(appkey, endpoint, channel,
 | 
			
		||||
                                                   rolename, rolesecret,
 | 
			
		||||
                                                   enablePerMessageDeflate);
 | 
			
		||||
                                                   enablePerMessageDeflate, socketTLSOptions);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Json::Value& CobraMetricsPublisher::getGenericAttributes()
 | 
			
		||||
 
 | 
			
		||||
@@ -15,6 +15,8 @@
 | 
			
		||||
 | 
			
		||||
namespace ix
 | 
			
		||||
{
 | 
			
		||||
    struct SocketTLSOptions;
 | 
			
		||||
 | 
			
		||||
    class CobraMetricsPublisher
 | 
			
		||||
    {
 | 
			
		||||
    public:
 | 
			
		||||
@@ -43,7 +45,8 @@ namespace ix
 | 
			
		||||
                       const std::string& channel,
 | 
			
		||||
                       const std::string& rolename,
 | 
			
		||||
                       const std::string& rolesecret,
 | 
			
		||||
                       bool enablePerMessageDeflate);
 | 
			
		||||
                       bool enablePerMessageDeflate,
 | 
			
		||||
                       const SocketTLSOptions& socketTLSOptions);
 | 
			
		||||
 | 
			
		||||
        /// Setter for the list of blacklisted metrics ids.
 | 
			
		||||
        /// That list is sorted internally for fast lookups
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,7 @@
 | 
			
		||||
 | 
			
		||||
#include "IXCobraMetricsThreadedPublisher.h"
 | 
			
		||||
#include <ixwebsocket/IXSetThreadName.h>
 | 
			
		||||
#include <ixwebsocket/IXSocketTLSOptions.h>
 | 
			
		||||
#include <ixcore/utils/IXCoreLogger.h>
 | 
			
		||||
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
@@ -92,14 +93,17 @@ namespace ix
 | 
			
		||||
                                                  const std::string& channel,
 | 
			
		||||
                                                  const std::string& rolename,
 | 
			
		||||
                                                  const std::string& rolesecret,
 | 
			
		||||
                                                  bool enablePerMessageDeflate)
 | 
			
		||||
                                                  bool enablePerMessageDeflate,
 | 
			
		||||
                                                  const SocketTLSOptions& socketTLSOptions)
 | 
			
		||||
    {
 | 
			
		||||
        _channel = channel;
 | 
			
		||||
 | 
			
		||||
        ix::IXCoreLogger::Log(socketTLSOptions.getDescription().c_str());
 | 
			
		||||
 | 
			
		||||
        ix::WebSocketPerMessageDeflateOptions webSocketPerMessageDeflateOptions(enablePerMessageDeflate);
 | 
			
		||||
        _cobra_connection.configure(appkey, endpoint,
 | 
			
		||||
                                    rolename, rolesecret,
 | 
			
		||||
                                    webSocketPerMessageDeflateOptions);
 | 
			
		||||
                                    webSocketPerMessageDeflateOptions, socketTLSOptions);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void CobraMetricsThreadedPublisher::pushMessage(MessageKind messageKind)
 | 
			
		||||
 
 | 
			
		||||
@@ -18,6 +18,8 @@
 | 
			
		||||
 | 
			
		||||
namespace ix
 | 
			
		||||
{
 | 
			
		||||
    struct SocketTLSOptions;
 | 
			
		||||
 | 
			
		||||
    class CobraMetricsThreadedPublisher
 | 
			
		||||
    {
 | 
			
		||||
    public:
 | 
			
		||||
@@ -30,7 +32,8 @@ namespace ix
 | 
			
		||||
                       const std::string& channel,
 | 
			
		||||
                       const std::string& rolename,
 | 
			
		||||
                       const std::string& rolesecret,
 | 
			
		||||
                       bool enablePerMessageDeflate);
 | 
			
		||||
                       bool enablePerMessageDeflate,
 | 
			
		||||
                       const SocketTLSOptions& socketTLSOptions);
 | 
			
		||||
 | 
			
		||||
        /// Start the worker thread, used for background publishing
 | 
			
		||||
        void start();
 | 
			
		||||
 
 | 
			
		||||
@@ -11,6 +11,7 @@
 | 
			
		||||
#include <fstream>
 | 
			
		||||
#include <sstream>
 | 
			
		||||
#include <ixwebsocket/IXWebSocketHttpHeaders.h>
 | 
			
		||||
#include <ixwebsocket/IXWebSocketVersion.h>
 | 
			
		||||
#include <ixcore/utils/IXCoreLogger.h>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -120,26 +121,6 @@ namespace ix
 | 
			
		||||
    {
 | 
			
		||||
        Json::Value payload;
 | 
			
		||||
 | 
			
		||||
        payload["platform"] = "python";
 | 
			
		||||
        payload["sdk"]["name"] = "ws";
 | 
			
		||||
        payload["sdk"]["version"] = "1.0.0";
 | 
			
		||||
        payload["timestamp"] = SentryClient::getIso8601();
 | 
			
		||||
 | 
			
		||||
        bool isNoisyTypes = msg["id"].asString() == "game_noisytypes_id";
 | 
			
		||||
 | 
			
		||||
        std::string stackTraceFieldName = isNoisyTypes ? "traceback" : "stack";
 | 
			
		||||
        std::string stack(msg["data"][stackTraceFieldName].asString());
 | 
			
		||||
 | 
			
		||||
        Json::Value exception;
 | 
			
		||||
        exception["stacktrace"]["frames"] = parseLuaStackTrace(stack);
 | 
			
		||||
        exception["value"] = isNoisyTypes ? parseExceptionName(stack) : msg["data"]["message"];
 | 
			
		||||
 | 
			
		||||
        payload["exception"].append(exception);
 | 
			
		||||
 | 
			
		||||
        Json::Value extra;
 | 
			
		||||
        extra["cobra_event"] = msg;
 | 
			
		||||
        extra["cobra_event"] = msg;
 | 
			
		||||
 | 
			
		||||
        //
 | 
			
		||||
        // "tags": [
 | 
			
		||||
        //   [
 | 
			
		||||
@@ -148,8 +129,61 @@ namespace ix
 | 
			
		||||
        //   ],
 | 
			
		||||
        //  ]
 | 
			
		||||
        //
 | 
			
		||||
        Json::Value tags;
 | 
			
		||||
        Json::Value tags(Json::arrayValue);
 | 
			
		||||
 | 
			
		||||
        payload["platform"] = "python";
 | 
			
		||||
        payload["sdk"]["name"] = "ws";
 | 
			
		||||
        payload["sdk"]["version"] = IX_WEBSOCKET_VERSION;
 | 
			
		||||
        payload["timestamp"] = SentryClient::getIso8601();
 | 
			
		||||
 | 
			
		||||
        bool isNoisyTypes = msg["id"].asString() == "game_noisytypes_id";
 | 
			
		||||
 | 
			
		||||
        std::string stackTraceFieldName = isNoisyTypes ? "traceback" : "stack";
 | 
			
		||||
        std::string stack;
 | 
			
		||||
        std::string message;
 | 
			
		||||
 | 
			
		||||
        if (isNoisyTypes)
 | 
			
		||||
        {
 | 
			
		||||
            stack = msg["data"][stackTraceFieldName].asString();
 | 
			
		||||
            message = parseExceptionName(stack);
 | 
			
		||||
        }
 | 
			
		||||
        else // logging
 | 
			
		||||
        {
 | 
			
		||||
            if (msg["data"].isMember("info"))
 | 
			
		||||
            {
 | 
			
		||||
                stack = msg["data"]["info"][stackTraceFieldName].asString();
 | 
			
		||||
                message = msg["data"]["info"]["message"].asString();
 | 
			
		||||
 | 
			
		||||
                if (msg["data"].isMember("tags"))
 | 
			
		||||
                {
 | 
			
		||||
                    auto members = msg["data"]["tags"].getMemberNames();
 | 
			
		||||
 | 
			
		||||
                    for (auto member : members)
 | 
			
		||||
                    {
 | 
			
		||||
                        Json::Value tag;
 | 
			
		||||
                        tag.append(member);
 | 
			
		||||
                        tag.append(msg["data"]["tags"][member]);
 | 
			
		||||
                        tags.append(tag);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                stack = msg["data"][stackTraceFieldName].asString();
 | 
			
		||||
                message = msg["data"]["message"].asString();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Json::Value exception;
 | 
			
		||||
        exception["stacktrace"]["frames"] = parseLuaStackTrace(stack);
 | 
			
		||||
        exception["value"] = message;
 | 
			
		||||
 | 
			
		||||
        payload["exception"].append(exception);
 | 
			
		||||
 | 
			
		||||
        Json::Value extra;
 | 
			
		||||
        extra["cobra_event"] = msg;
 | 
			
		||||
 | 
			
		||||
        // Builtin tags
 | 
			
		||||
        Json::Value gameTag;
 | 
			
		||||
        gameTag.append("game");
 | 
			
		||||
        gameTag.append(msg["device"]["game"]);
 | 
			
		||||
@@ -165,6 +199,11 @@ namespace ix
 | 
			
		||||
        environmentTag.append(msg["device"]["environment"]);
 | 
			
		||||
        tags.append(environmentTag);
 | 
			
		||||
 | 
			
		||||
        Json::Value clientVersionTag;
 | 
			
		||||
        clientVersionTag.append("client_version");
 | 
			
		||||
        clientVersionTag.append(msg["device"]["app_version"]);
 | 
			
		||||
        tags.append(clientVersionTag);
 | 
			
		||||
 | 
			
		||||
        payload["tags"] = tags;
 | 
			
		||||
 | 
			
		||||
        return _jsonWriter.write(payload);
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,7 @@
 | 
			
		||||
#include <nlohmann/json.hpp>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <ixwebsocket/IXSocketTLSOptions.h>
 | 
			
		||||
 | 
			
		||||
namespace snake
 | 
			
		||||
{
 | 
			
		||||
@@ -26,6 +27,9 @@ namespace snake
 | 
			
		||||
        // AppKeys
 | 
			
		||||
        nlohmann::json apps;
 | 
			
		||||
 | 
			
		||||
        // TLS options
 | 
			
		||||
        ix::SocketTLSOptions socketTLSOptions;
 | 
			
		||||
 | 
			
		||||
        // Misc
 | 
			
		||||
        bool verbose;
 | 
			
		||||
    };
 | 
			
		||||
 
 | 
			
		||||
@@ -20,7 +20,7 @@ namespace snake
 | 
			
		||||
        : _appConfig(appConfig)
 | 
			
		||||
        , _server(appConfig.port, appConfig.hostname)
 | 
			
		||||
    {
 | 
			
		||||
        ;
 | 
			
		||||
        _server.setTLSOptions(appConfig.socketTLSOptions);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //
 | 
			
		||||
 
 | 
			
		||||
@@ -24,9 +24,47 @@
 | 
			
		||||
 | 
			
		||||
#include <Security/SecureTransport.h>
 | 
			
		||||
 | 
			
		||||
namespace
 | 
			
		||||
namespace ix
 | 
			
		||||
{
 | 
			
		||||
    OSStatus read_from_socket(SSLConnectionRef connection, void* data, size_t* len)
 | 
			
		||||
    SocketAppleSSL::SocketAppleSSL(const SocketTLSOptions& tlsOptions, int fd)
 | 
			
		||||
        : Socket(fd)
 | 
			
		||||
        , _sslContext(nullptr)
 | 
			
		||||
        , _tlsOptions(tlsOptions)
 | 
			
		||||
    {
 | 
			
		||||
        ;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    SocketAppleSSL::~SocketAppleSSL()
 | 
			
		||||
    {
 | 
			
		||||
        SocketAppleSSL::close();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::string SocketAppleSSL::getSSLErrorDescription(OSStatus status)
 | 
			
		||||
    {
 | 
			
		||||
        std::string errMsg("Unknown SSL error.");
 | 
			
		||||
 | 
			
		||||
        CFErrorRef error = CFErrorCreate(kCFAllocatorDefault, kCFErrorDomainOSStatus, status, NULL);
 | 
			
		||||
        if (error)
 | 
			
		||||
        {
 | 
			
		||||
            CFStringRef message = CFErrorCopyDescription(error);
 | 
			
		||||
            if (message)
 | 
			
		||||
            {
 | 
			
		||||
                char localBuffer[128];
 | 
			
		||||
                Boolean success;
 | 
			
		||||
                success = CFStringGetCString(message, localBuffer, 128, kCFStringEncodingUTF8);
 | 
			
		||||
                if (success)
 | 
			
		||||
                {
 | 
			
		||||
                    errMsg = localBuffer;
 | 
			
		||||
                }
 | 
			
		||||
                CFRelease(message);
 | 
			
		||||
            }
 | 
			
		||||
            CFRelease(error);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return errMsg;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    OSStatus SocketAppleSSL::readFromSocket(SSLConnectionRef connection, void* data, size_t* len)
 | 
			
		||||
    {
 | 
			
		||||
        int fd = (int) (long) connection;
 | 
			
		||||
        if (fd < 0) return errSSLInternal;
 | 
			
		||||
@@ -67,7 +105,7 @@ namespace
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    OSStatus write_to_socket(SSLConnectionRef connection, const void* data, size_t* len)
 | 
			
		||||
    OSStatus SocketAppleSSL::writeToSocket(SSLConnectionRef connection, const void* data, size_t* len)
 | 
			
		||||
    {
 | 
			
		||||
        int fd = (int) (long) connection;
 | 
			
		||||
        if (fd < 0) return errSSLInternal;
 | 
			
		||||
@@ -105,47 +143,55 @@ namespace
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::string getSSLErrorDescription(OSStatus status)
 | 
			
		||||
    {
 | 
			
		||||
        std::string errMsg("Unknown SSL error.");
 | 
			
		||||
 | 
			
		||||
        CFErrorRef error = CFErrorCreate(kCFAllocatorDefault, kCFErrorDomainOSStatus, status, NULL);
 | 
			
		||||
        if (error)
 | 
			
		||||
    bool SocketAppleSSL::accept(std::string& errMsg)
 | 
			
		||||
    {
 | 
			
		||||
        OSStatus status;
 | 
			
		||||
        {
 | 
			
		||||
            CFStringRef message = CFErrorCopyDescription(error);
 | 
			
		||||
            if (message)
 | 
			
		||||
            std::lock_guard<std::mutex> lock(_mutex);
 | 
			
		||||
 | 
			
		||||
            _sslContext = SSLCreateContext(kCFAllocatorDefault, kSSLServerSide, kSSLStreamType);
 | 
			
		||||
 | 
			
		||||
            SSLSetIOFuncs(_sslContext, SocketAppleSSL::readFromSocket, SocketAppleSSL::writeToSocket);
 | 
			
		||||
            SSLSetConnection(_sslContext, (SSLConnectionRef)(long) _sockfd);
 | 
			
		||||
            SSLSetProtocolVersionMin(_sslContext, kTLSProtocol12);
 | 
			
		||||
 | 
			
		||||
            if (_tlsOptions.isPeerVerifyDisabled())
 | 
			
		||||
            {
 | 
			
		||||
                char localBuffer[128];
 | 
			
		||||
                Boolean success;
 | 
			
		||||
                success =
 | 
			
		||||
                    CFStringGetCString(message, localBuffer, 128, CFStringGetSystemEncoding());
 | 
			
		||||
                if (success)
 | 
			
		||||
                Boolean option(1);
 | 
			
		||||
                SSLSetSessionOption(_sslContext, kSSLSessionOptionBreakOnServerAuth, option);
 | 
			
		||||
 | 
			
		||||
                do
 | 
			
		||||
                {
 | 
			
		||||
                    errMsg = localBuffer;
 | 
			
		||||
                    status = SSLHandshake(_sslContext);
 | 
			
		||||
                } while (errSSLWouldBlock == status || errSSLServerAuthCompleted == status);
 | 
			
		||||
 | 
			
		||||
                if (status == errSSLServerAuthCompleted)
 | 
			
		||||
                {
 | 
			
		||||
                    // proceed with the handshake
 | 
			
		||||
                    do
 | 
			
		||||
                    {
 | 
			
		||||
                        status = SSLHandshake(_sslContext);
 | 
			
		||||
                    } while (errSSLWouldBlock == status || errSSLServerAuthCompleted == status);
 | 
			
		||||
                }
 | 
			
		||||
                CFRelease(message);
 | 
			
		||||
            }
 | 
			
		||||
            CFRelease(error);
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                do
 | 
			
		||||
                {
 | 
			
		||||
                    status = SSLHandshake(_sslContext);
 | 
			
		||||
                } while (errSSLWouldBlock == status || errSSLServerAuthCompleted == status);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return errMsg;
 | 
			
		||||
    }
 | 
			
		||||
        if (noErr != status)
 | 
			
		||||
        {
 | 
			
		||||
            errMsg = getSSLErrorDescription(status);
 | 
			
		||||
            close();
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
} // anonymous namespace
 | 
			
		||||
 | 
			
		||||
namespace ix
 | 
			
		||||
{
 | 
			
		||||
    SocketAppleSSL::SocketAppleSSL(const SocketTLSOptions& tlsOptions, int fd)
 | 
			
		||||
        : Socket(fd)
 | 
			
		||||
        , _sslContext(nullptr)
 | 
			
		||||
        , _tlsOptions(tlsOptions)
 | 
			
		||||
    {
 | 
			
		||||
        ;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    SocketAppleSSL::~SocketAppleSSL()
 | 
			
		||||
    {
 | 
			
		||||
        SocketAppleSSL::close();
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // No wait support
 | 
			
		||||
@@ -163,7 +209,7 @@ namespace ix
 | 
			
		||||
 | 
			
		||||
            _sslContext = SSLCreateContext(kCFAllocatorDefault, kSSLClientSide, kSSLStreamType);
 | 
			
		||||
 | 
			
		||||
            SSLSetIOFuncs(_sslContext, read_from_socket, write_to_socket);
 | 
			
		||||
            SSLSetIOFuncs(_sslContext, SocketAppleSSL::readFromSocket, SocketAppleSSL::writeToSocket);
 | 
			
		||||
            SSLSetConnection(_sslContext, (SSLConnectionRef)(long) _sockfd);
 | 
			
		||||
            SSLSetProtocolVersionMin(_sslContext, kTLSProtocol12);
 | 
			
		||||
            SSLSetPeerDomainName(_sslContext, host.c_str(), host.size());
 | 
			
		||||
 
 | 
			
		||||
@@ -21,6 +21,8 @@ namespace ix
 | 
			
		||||
        SocketAppleSSL(const SocketTLSOptions& tlsOptions, int fd = -1);
 | 
			
		||||
        ~SocketAppleSSL();
 | 
			
		||||
 | 
			
		||||
        virtual bool accept(std::string& errMsg) final;
 | 
			
		||||
 | 
			
		||||
        virtual bool connect(const std::string& host,
 | 
			
		||||
                             int port,
 | 
			
		||||
                             std::string& errMsg,
 | 
			
		||||
@@ -32,6 +34,10 @@ namespace ix
 | 
			
		||||
        virtual ssize_t recv(void* buffer, size_t length) final;
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        static std::string getSSLErrorDescription(OSStatus status);
 | 
			
		||||
        static OSStatus writeToSocket(SSLConnectionRef connection, const void* data, size_t* len);
 | 
			
		||||
        static OSStatus readFromSocket(SSLConnectionRef connection, void* data, size_t* len);
 | 
			
		||||
 | 
			
		||||
        SSLContextRef _sslContext;
 | 
			
		||||
        mutable std::mutex _mutex; // AppleSSL routines are not thread-safe
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -38,9 +38,10 @@ namespace ix
 | 
			
		||||
        mbedtls_ctr_drbg_init(&_ctr_drbg);
 | 
			
		||||
        mbedtls_entropy_init(&_entropy);
 | 
			
		||||
        mbedtls_x509_crt_init(&_cacert);
 | 
			
		||||
        mbedtls_x509_crt_init(&_cert);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool SocketMbedTLS::init(const std::string& host, std::string& errMsg)
 | 
			
		||||
    bool SocketMbedTLS::init(const std::string& host, bool isClient, std::string& errMsg)
 | 
			
		||||
    {
 | 
			
		||||
        initMBedTLS();
 | 
			
		||||
        std::lock_guard<std::mutex> lock(_mutex);
 | 
			
		||||
@@ -58,7 +59,7 @@ namespace ix
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (mbedtls_ssl_config_defaults(&_conf,
 | 
			
		||||
                                        MBEDTLS_SSL_IS_CLIENT,
 | 
			
		||||
                                        (isClient) ? MBEDTLS_SSL_IS_CLIENT : MBEDTLS_SSL_IS_SERVER,
 | 
			
		||||
                                        MBEDTLS_SSL_TRANSPORT_STREAM,
 | 
			
		||||
                                        MBEDTLS_SSL_PRESET_DEFAULT) != 0)
 | 
			
		||||
        {
 | 
			
		||||
@@ -68,13 +69,27 @@ namespace ix
 | 
			
		||||
 | 
			
		||||
        mbedtls_ssl_conf_rng(&_conf, mbedtls_ctr_drbg_random, &_ctr_drbg);
 | 
			
		||||
 | 
			
		||||
        if (_tlsOptions.hasCertAndKey())
 | 
			
		||||
        {
 | 
			
		||||
            if (mbedtls_x509_crt_parse_file(&_cert, _tlsOptions.certFile.c_str()) < 0)
 | 
			
		||||
            {
 | 
			
		||||
                errMsg = "Cannot parse cert file '" + _tlsOptions.certFile + "'";
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
            if (mbedtls_pk_parse_keyfile(&_pkey, _tlsOptions.keyFile.c_str(), "") < 0)
 | 
			
		||||
            {
 | 
			
		||||
                errMsg = "Cannot parse key file '" + _tlsOptions.keyFile + "'";
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (_tlsOptions.isPeerVerifyDisabled())
 | 
			
		||||
        {
 | 
			
		||||
            mbedtls_ssl_conf_authmode(&_conf, MBEDTLS_SSL_VERIFY_NONE);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            mbedtls_ssl_conf_ca_chain(&_conf, &_cacert, NULL);
 | 
			
		||||
            mbedtls_ssl_conf_authmode(&_conf, MBEDTLS_SSL_VERIFY_REQUIRED);
 | 
			
		||||
 | 
			
		||||
            // FIXME: should we call mbedtls_ssl_conf_verify ?
 | 
			
		||||
 | 
			
		||||
@@ -87,7 +102,13 @@ namespace ix
 | 
			
		||||
                errMsg = "Cannot parse CA file '" + _tlsOptions.caFile + "'";
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
            mbedtls_ssl_conf_authmode(&_conf, MBEDTLS_SSL_VERIFY_REQUIRED);
 | 
			
		||||
 | 
			
		||||
            mbedtls_ssl_conf_ca_chain(&_conf, &_cacert, NULL);
 | 
			
		||||
 | 
			
		||||
            if (_tlsOptions.hasCertAndKey())
 | 
			
		||||
            {
 | 
			
		||||
                mbedtls_ssl_conf_own_cert(&_conf, &_cert, &_pkey);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (mbedtls_ssl_setup(&_ssl, &_conf) != 0)
 | 
			
		||||
@@ -96,7 +117,7 @@ namespace ix
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (mbedtls_ssl_set_hostname(&_ssl, host.c_str()) != 0)
 | 
			
		||||
        if (!host.empty() && mbedtls_ssl_set_hostname(&_ssl, host.c_str()) != 0)
 | 
			
		||||
        {
 | 
			
		||||
            errMsg = "SNI setup failed";
 | 
			
		||||
            return false;
 | 
			
		||||
@@ -105,6 +126,50 @@ namespace ix
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool SocketMbedTLS::accept(std::string& errMsg)
 | 
			
		||||
    {
 | 
			
		||||
        bool isClient = false;
 | 
			
		||||
        bool initialized = init(std::string(), isClient, errMsg);
 | 
			
		||||
        if (!initialized)
 | 
			
		||||
        {
 | 
			
		||||
            close();
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        mbedtls_ssl_set_bio(&_ssl, &_sockfd, mbedtls_net_send, mbedtls_net_recv, NULL);
 | 
			
		||||
 | 
			
		||||
        int res;
 | 
			
		||||
        do
 | 
			
		||||
        {
 | 
			
		||||
            std::lock_guard<std::mutex> lock(_mutex);
 | 
			
		||||
            res = mbedtls_ssl_handshake(&_ssl);
 | 
			
		||||
        } while (res == MBEDTLS_ERR_SSL_WANT_READ || res == MBEDTLS_ERR_SSL_WANT_WRITE);
 | 
			
		||||
 | 
			
		||||
        if (res != 0)
 | 
			
		||||
        {
 | 
			
		||||
            char buf[256];
 | 
			
		||||
            mbedtls_strerror(res, buf, sizeof(buf));
 | 
			
		||||
 | 
			
		||||
            errMsg = "error in handshake : ";
 | 
			
		||||
            errMsg += buf;
 | 
			
		||||
 | 
			
		||||
            if (res == MBEDTLS_ERR_X509_CERT_VERIFY_FAILED)
 | 
			
		||||
            {
 | 
			
		||||
                char verifyBuf[512];
 | 
			
		||||
                uint32_t flags = mbedtls_ssl_get_verify_result(&_ssl);
 | 
			
		||||
 | 
			
		||||
                mbedtls_x509_crt_verify_info(verifyBuf, sizeof(verifyBuf), "  ! ", flags);
 | 
			
		||||
                errMsg += " : ";
 | 
			
		||||
                errMsg += verifyBuf;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            close();
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool SocketMbedTLS::connect(const std::string& host,
 | 
			
		||||
                                int port,
 | 
			
		||||
                                std::string& errMsg,
 | 
			
		||||
@@ -116,7 +181,8 @@ namespace ix
 | 
			
		||||
            if (_sockfd == -1) return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        bool initialized = init(host, errMsg);
 | 
			
		||||
        bool isClient = true;
 | 
			
		||||
        bool initialized = init(host, isClient, errMsg);
 | 
			
		||||
        if (!initialized)
 | 
			
		||||
        {
 | 
			
		||||
            close();
 | 
			
		||||
@@ -156,6 +222,7 @@ namespace ix
 | 
			
		||||
        mbedtls_ctr_drbg_free(&_ctr_drbg);
 | 
			
		||||
        mbedtls_entropy_free(&_entropy);
 | 
			
		||||
        mbedtls_x509_crt_free(&_cacert);
 | 
			
		||||
        mbedtls_x509_crt_free(&_cert);
 | 
			
		||||
 | 
			
		||||
        Socket::close();
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -26,6 +26,8 @@ namespace ix
 | 
			
		||||
        SocketMbedTLS(const SocketTLSOptions& tlsOptions, int fd = -1);
 | 
			
		||||
        ~SocketMbedTLS();
 | 
			
		||||
 | 
			
		||||
        virtual bool accept(std::string& errMsg) final;
 | 
			
		||||
 | 
			
		||||
        virtual bool connect(const std::string& host,
 | 
			
		||||
                             int port,
 | 
			
		||||
                             std::string& errMsg,
 | 
			
		||||
@@ -42,11 +44,13 @@ namespace ix
 | 
			
		||||
        mbedtls_entropy_context _entropy;
 | 
			
		||||
        mbedtls_ctr_drbg_context _ctr_drbg;
 | 
			
		||||
        mbedtls_x509_crt _cacert;
 | 
			
		||||
        mbedtls_x509_crt _cert;
 | 
			
		||||
        mbedtls_pk_context _pkey;
 | 
			
		||||
 | 
			
		||||
        std::mutex _mutex;
 | 
			
		||||
        SocketTLSOptions _tlsOptions;
 | 
			
		||||
 | 
			
		||||
        bool init(const std::string& host, std::string& errMsg);
 | 
			
		||||
        bool init(const std::string& host, bool isClient, std::string& errMsg);
 | 
			
		||||
        void initMBedTLS();
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@
 | 
			
		||||
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <fstream>
 | 
			
		||||
#include <sstream>
 | 
			
		||||
 | 
			
		||||
namespace ix
 | 
			
		||||
{
 | 
			
		||||
@@ -71,4 +72,16 @@ namespace ix
 | 
			
		||||
    {
 | 
			
		||||
        return _errMsg;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::string SocketTLSOptions::getDescription() const
 | 
			
		||||
    {
 | 
			
		||||
        std::stringstream ss;
 | 
			
		||||
        ss << "TLS Options:" << std::endl;
 | 
			
		||||
        ss << "  certFile = " << certFile << std::endl;
 | 
			
		||||
        ss << "  keyFile  = " << keyFile << std::endl;
 | 
			
		||||
        ss << "  caFile   = " << caFile << std::endl;
 | 
			
		||||
        ss << "  ciphers  = " << ciphers << std::endl;
 | 
			
		||||
        ss << "  ciphers  = " << ciphers << std::endl;
 | 
			
		||||
        return ss.str();
 | 
			
		||||
    }
 | 
			
		||||
} // namespace ix
 | 
			
		||||
 
 | 
			
		||||
@@ -43,8 +43,10 @@ namespace ix
 | 
			
		||||
 | 
			
		||||
        const std::string& getErrorMsg() const;
 | 
			
		||||
 | 
			
		||||
        std::string getDescription() const;
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        mutable std::string _errMsg;
 | 
			
		||||
        mutable bool _validated;
 | 
			
		||||
        mutable bool _validated = false;
 | 
			
		||||
    };
 | 
			
		||||
} // namespace ix
 | 
			
		||||
 
 | 
			
		||||
@@ -178,8 +178,8 @@ namespace ix
 | 
			
		||||
        if (status != 101)
 | 
			
		||||
        {
 | 
			
		||||
            std::stringstream ss;
 | 
			
		||||
            ss << "Got bad status connecting to " << host << ":" << port << ", status: " << status
 | 
			
		||||
               << ", HTTP Status line: " << line;
 | 
			
		||||
            ss << "Expecting status 101 (Switching Protocol), got " << status
 | 
			
		||||
               << " status connecting to " << host << ":" << port << ", HTTP Status line: " << line;
 | 
			
		||||
            return WebSocketInitResult(false, status, ss.str());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -77,6 +77,7 @@ namespace ix
 | 
			
		||||
 | 
			
		||||
    WebSocketTransport::WebSocketTransport()
 | 
			
		||||
        : _useMask(true)
 | 
			
		||||
        , _blockingSend(false)
 | 
			
		||||
        , _compressedMessage(false)
 | 
			
		||||
        , _readyState(ReadyState::CLOSED)
 | 
			
		||||
        , _closeCode(WebSocketCloseConstants::kInternalErrorCode)
 | 
			
		||||
@@ -143,7 +144,9 @@ namespace ix
 | 
			
		||||
 | 
			
		||||
        if (!UrlParser::parse(url, protocol, host, path, query, port))
 | 
			
		||||
        {
 | 
			
		||||
            return WebSocketInitResult(false, 0, std::string("Could not parse URL ") + url);
 | 
			
		||||
            std::stringstream ss;
 | 
			
		||||
            ss << "Could not parse url: '" << url << "'";
 | 
			
		||||
            return WebSocketInitResult(false, 0, ss.str());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        std::string errorMsg;
 | 
			
		||||
@@ -178,6 +181,7 @@ namespace ix
 | 
			
		||||
 | 
			
		||||
        // Server should not mask the data it sends to the client
 | 
			
		||||
        _useMask = false;
 | 
			
		||||
        _blockingSend = true;
 | 
			
		||||
 | 
			
		||||
        _socket = socket;
 | 
			
		||||
 | 
			
		||||
@@ -339,22 +343,9 @@ namespace ix
 | 
			
		||||
        // there can be a lot of it for large messages.
 | 
			
		||||
        if (pollResult == PollResultType::SendRequest)
 | 
			
		||||
        {
 | 
			
		||||
            while (!isSendBufferEmpty() && !_requestInitCancellation)
 | 
			
		||||
            if (!flushSendBuffer())
 | 
			
		||||
            {
 | 
			
		||||
                // Wait with a 10ms timeout until the socket is ready to write.
 | 
			
		||||
                // This way we are not busy looping
 | 
			
		||||
                PollResultType result = _socket->isReadyToWrite(10);
 | 
			
		||||
 | 
			
		||||
                if (result == PollResultType::Error)
 | 
			
		||||
                {
 | 
			
		||||
                    closeSocket();
 | 
			
		||||
                    setReadyState(ReadyState::CLOSED);
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                else if (result == PollResultType::ReadyForWrite)
 | 
			
		||||
                {
 | 
			
		||||
                    sendOnSocket();
 | 
			
		||||
                }
 | 
			
		||||
                return PollResult::CannotFlushSendBuffer;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else if (pollResult == PollResultType::ReadyForRead)
 | 
			
		||||
@@ -924,13 +915,21 @@ namespace ix
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        bool success = true;
 | 
			
		||||
 | 
			
		||||
        // Request to flush the send buffer on the background thread if it isn't empty
 | 
			
		||||
        if (!isSendBufferEmpty())
 | 
			
		||||
        {
 | 
			
		||||
            _socket->wakeUpFromPoll(Socket::kSendRequest);
 | 
			
		||||
 | 
			
		||||
            // FIXME: we should have a timeout when sending large messages: see #131
 | 
			
		||||
            if (_blockingSend && !flushSendBuffer())
 | 
			
		||||
            {
 | 
			
		||||
                success = false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return WebSocketSendInfo(true, compressionError, payloadSize, wireSize);
 | 
			
		||||
        return WebSocketSendInfo(success, compressionError, payloadSize, wireSize);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void WebSocketTransport::sendFragment(wsheader_type::opcode_type type,
 | 
			
		||||
@@ -1168,4 +1167,27 @@ namespace ix
 | 
			
		||||
        return _txbuf.size();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool WebSocketTransport::flushSendBuffer()
 | 
			
		||||
    {
 | 
			
		||||
        while (!isSendBufferEmpty() && !_requestInitCancellation)
 | 
			
		||||
        {
 | 
			
		||||
            // Wait with a 10ms timeout until the socket is ready to write.
 | 
			
		||||
            // This way we are not busy looping
 | 
			
		||||
            PollResultType result = _socket->isReadyToWrite(10);
 | 
			
		||||
 | 
			
		||||
            if (result == PollResultType::Error)
 | 
			
		||||
            {
 | 
			
		||||
                closeSocket();
 | 
			
		||||
                setReadyState(ReadyState::CLOSED);
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
            else if (result == PollResultType::ReadyForWrite)
 | 
			
		||||
            {
 | 
			
		||||
                sendOnSocket();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
} // namespace ix
 | 
			
		||||
 
 | 
			
		||||
@@ -61,7 +61,8 @@ namespace ix
 | 
			
		||||
        enum class PollResult
 | 
			
		||||
        {
 | 
			
		||||
            Succeeded,
 | 
			
		||||
            AbnormalClose
 | 
			
		||||
            AbnormalClose,
 | 
			
		||||
            CannotFlushSendBuffer
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        using OnMessageCallback =
 | 
			
		||||
@@ -135,6 +136,10 @@ namespace ix
 | 
			
		||||
        // client should mask but server should not
 | 
			
		||||
        std::atomic<bool> _useMask;
 | 
			
		||||
 | 
			
		||||
        // Tells whether we should flush the send buffer before
 | 
			
		||||
        // saying that a send is complete. This is the mode for server code.
 | 
			
		||||
        std::atomic<bool> _blockingSend;
 | 
			
		||||
 | 
			
		||||
        // Buffer for reading from our socket. That buffer is never resized.
 | 
			
		||||
        std::vector<uint8_t> _readbuf;
 | 
			
		||||
 | 
			
		||||
@@ -238,6 +243,7 @@ namespace ix
 | 
			
		||||
                                               size_t closeWireSize,
 | 
			
		||||
                                               bool remote);
 | 
			
		||||
 | 
			
		||||
        bool flushSendBuffer();
 | 
			
		||||
        void sendOnSocket();
 | 
			
		||||
        WebSocketSendInfo sendData(wsheader_type::opcode_type type,
 | 
			
		||||
                                   const std::string& message,
 | 
			
		||||
 
 | 
			
		||||
@@ -6,4 +6,4 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#define IX_WEBSOCKET_VERSION "7.5.0"
 | 
			
		||||
#define IX_WEBSOCKET_VERSION "7.6.4"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										9
									
								
								makefile
									
									
									
									
									
								
							
							
						
						
									
										9
									
								
								makefile
									
									
									
									
									
								
							@@ -30,7 +30,7 @@ uninstall:
 | 
			
		||||
	xargs rm -fv < build/install_manifest.txt
 | 
			
		||||
 | 
			
		||||
tag:
 | 
			
		||||
	git tag v"`cat DOCKER_VERSION`"
 | 
			
		||||
	git tag v"`sh tools/extract_version.sh`"
 | 
			
		||||
 | 
			
		||||
xcode:
 | 
			
		||||
	cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 -DUSE_TEST=1 -GXcode && open ixwebsocket.xcodeproj
 | 
			
		||||
@@ -40,12 +40,15 @@ xcode_openssl:
 | 
			
		||||
 | 
			
		||||
.PHONY: docker
 | 
			
		||||
 | 
			
		||||
NAME   := bsergean/ws
 | 
			
		||||
TAG    := $(shell cat DOCKER_VERSION)
 | 
			
		||||
NAME   := ${DOCKER_REPO}/ws
 | 
			
		||||
TAG    := $(shell sh tools/extract_version.sh)
 | 
			
		||||
IMG    := ${NAME}:${TAG}
 | 
			
		||||
LATEST := ${NAME}:latest
 | 
			
		||||
BUILD  := ${NAME}:build
 | 
			
		||||
 | 
			
		||||
print_version:
 | 
			
		||||
	@echo 'IXWebSocket version =>' ${TAG}
 | 
			
		||||
 | 
			
		||||
docker_test:
 | 
			
		||||
	docker build -f docker/Dockerfile.debian -t bsergean/ixwebsocket_test:build .
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -34,10 +34,10 @@ namespace
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    class SatoriChat
 | 
			
		||||
    class CobraChat
 | 
			
		||||
    {
 | 
			
		||||
    public:
 | 
			
		||||
        SatoriChat(const std::string& user,
 | 
			
		||||
        CobraChat(const std::string& user,
 | 
			
		||||
                   const std::string& session,
 | 
			
		||||
                   const std::string& endpoint);
 | 
			
		||||
 | 
			
		||||
@@ -72,9 +72,9 @@ namespace
 | 
			
		||||
        std::mutex _logMutex;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    SatoriChat::SatoriChat(const std::string& user,
 | 
			
		||||
                           const std::string& session,
 | 
			
		||||
                           const std::string& endpoint)
 | 
			
		||||
    CobraChat::CobraChat(const std::string& user,
 | 
			
		||||
                         const std::string& session,
 | 
			
		||||
                         const std::string& endpoint)
 | 
			
		||||
        : _user(user)
 | 
			
		||||
        , _session(session)
 | 
			
		||||
        , _endpoint(endpoint)
 | 
			
		||||
@@ -83,34 +83,34 @@ namespace
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void SatoriChat::start()
 | 
			
		||||
    void CobraChat::start()
 | 
			
		||||
    {
 | 
			
		||||
        _thread = std::thread(&SatoriChat::run, this);
 | 
			
		||||
        _thread = std::thread(&CobraChat::run, this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void SatoriChat::stop()
 | 
			
		||||
    void CobraChat::stop()
 | 
			
		||||
    {
 | 
			
		||||
        _stop = true;
 | 
			
		||||
        _thread.join();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool SatoriChat::isReady() const
 | 
			
		||||
    bool CobraChat::isReady() const
 | 
			
		||||
    {
 | 
			
		||||
        return _connectedAndSubscribed;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    size_t SatoriChat::getReceivedMessagesCount() const
 | 
			
		||||
    size_t CobraChat::getReceivedMessagesCount() const
 | 
			
		||||
    {
 | 
			
		||||
        return _receivedQueue.size();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool SatoriChat::hasPendingMessages() const
 | 
			
		||||
    bool CobraChat::hasPendingMessages() const
 | 
			
		||||
    {
 | 
			
		||||
        std::unique_lock<std::mutex> lock(_queue_mutex);
 | 
			
		||||
        return !_publish_queue.empty();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Json::Value SatoriChat::popMessage()
 | 
			
		||||
    Json::Value CobraChat::popMessage()
 | 
			
		||||
    {
 | 
			
		||||
        std::unique_lock<std::mutex> lock(_queue_mutex);
 | 
			
		||||
        auto msg = _publish_queue.front();
 | 
			
		||||
@@ -121,7 +121,7 @@ namespace
 | 
			
		||||
    //
 | 
			
		||||
    // Callback to handle received messages, that are printed on the console
 | 
			
		||||
    //
 | 
			
		||||
    void SatoriChat::subscribe(const std::string& channel)
 | 
			
		||||
    void CobraChat::subscribe(const std::string& channel)
 | 
			
		||||
    {
 | 
			
		||||
        std::string filter;
 | 
			
		||||
        _conn.subscribe(channel, filter, [this](const Json::Value& msg) {
 | 
			
		||||
@@ -151,7 +151,7 @@ namespace
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void SatoriChat::sendMessage(const std::string& text)
 | 
			
		||||
    void CobraChat::sendMessage(const std::string& text)
 | 
			
		||||
    {
 | 
			
		||||
        Json::Value msg;
 | 
			
		||||
        msg["user"] = _user;
 | 
			
		||||
@@ -166,16 +166,21 @@ namespace
 | 
			
		||||
    // Do satori communication on a background thread, where we can have
 | 
			
		||||
    // something like an event loop that publish, poll and receive data
 | 
			
		||||
    //
 | 
			
		||||
    void SatoriChat::run()
 | 
			
		||||
    void CobraChat::run()
 | 
			
		||||
    {
 | 
			
		||||
        // "chat" conf
 | 
			
		||||
        std::string appkey("FC2F10139A2BAc53BB72D9db967b024f");
 | 
			
		||||
        std::string channel = _session;
 | 
			
		||||
        std::string role = "_sub";
 | 
			
		||||
        std::string secret = "66B1dA3ED5fA074EB5AE84Dd8CE3b5ba";
 | 
			
		||||
        SocketTLSOptions socketTLSOptions;
 | 
			
		||||
 | 
			
		||||
        _conn.configure(
 | 
			
		||||
            appkey, _endpoint, role, secret, ix::WebSocketPerMessageDeflateOptions(true));
 | 
			
		||||
        _conn.configure(appkey,
 | 
			
		||||
                        _endpoint,
 | 
			
		||||
                        role,
 | 
			
		||||
                        secret,
 | 
			
		||||
                        ix::WebSocketPerMessageDeflateOptions(true),
 | 
			
		||||
                        socketTLSOptions);
 | 
			
		||||
        _conn.connect();
 | 
			
		||||
 | 
			
		||||
        _conn.setEventCallback([this, channel](ix::CobraConnectionEventType eventType,
 | 
			
		||||
@@ -280,8 +285,8 @@ TEST_CASE("Cobra_chat", "[cobra_chat]")
 | 
			
		||||
        ss << "ws://localhost:" << port;
 | 
			
		||||
        std::string endpoint = ss.str();
 | 
			
		||||
 | 
			
		||||
        SatoriChat chatA("jean", session, endpoint);
 | 
			
		||||
        SatoriChat chatB("paul", session, endpoint);
 | 
			
		||||
        CobraChat chatA("jean", session, endpoint);
 | 
			
		||||
        CobraChat chatB("paul", session, endpoint);
 | 
			
		||||
 | 
			
		||||
        chatA.start();
 | 
			
		||||
        chatB.start();
 | 
			
		||||
 
 | 
			
		||||
@@ -62,11 +62,14 @@ namespace
 | 
			
		||||
        gMessageCount = 0;
 | 
			
		||||
 | 
			
		||||
        ix::CobraConnection conn;
 | 
			
		||||
        SocketTLSOptions socketTLSOptions;
 | 
			
		||||
 | 
			
		||||
        conn.configure(APPKEY,
 | 
			
		||||
                       endpoint,
 | 
			
		||||
                       SUBSCRIBER_ROLE,
 | 
			
		||||
                       SUBSCRIBER_SECRET,
 | 
			
		||||
                       ix::WebSocketPerMessageDeflateOptions(true));
 | 
			
		||||
                       ix::WebSocketPerMessageDeflateOptions(true),
 | 
			
		||||
                       socketTLSOptions);
 | 
			
		||||
        conn.connect();
 | 
			
		||||
 | 
			
		||||
        conn.setEventCallback([&conn](ix::CobraConnectionEventType eventType,
 | 
			
		||||
@@ -202,9 +205,15 @@ TEST_CASE("Cobra_Metrics_Publisher", "[cobra]")
 | 
			
		||||
 | 
			
		||||
    ix::CobraMetricsPublisher cobraMetricsPublisher;
 | 
			
		||||
 | 
			
		||||
    SocketTLSOptions socketTLSOptions;
 | 
			
		||||
    bool perMessageDeflate = true;
 | 
			
		||||
    cobraMetricsPublisher.configure(
 | 
			
		||||
        APPKEY, endpoint, CHANNEL, PUBLISHER_ROLE, PUBLISHER_SECRET, perMessageDeflate);
 | 
			
		||||
    cobraMetricsPublisher.configure(APPKEY,
 | 
			
		||||
                                    endpoint,
 | 
			
		||||
                                    CHANNEL,
 | 
			
		||||
                                    PUBLISHER_ROLE,
 | 
			
		||||
                                    PUBLISHER_SECRET,
 | 
			
		||||
                                    perMessageDeflate,
 | 
			
		||||
                                    socketTLSOptions);
 | 
			
		||||
    cobraMetricsPublisher.setSession(uuid4());
 | 
			
		||||
    cobraMetricsPublisher.enable(true); // disabled by default, needs to be enabled to be active
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										97
									
								
								third_party/spdlog/CMakeLists.txt
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										97
									
								
								third_party/spdlog/CMakeLists.txt
									
									
									
									
										vendored
									
									
								
							@@ -63,31 +63,43 @@ option(SPDLOG_SANITIZE_ADDRESS "Enable address sanitizer in tests" OFF)
 | 
			
		||||
# install options
 | 
			
		||||
option(SPDLOG_INSTALL "Generate the install target" ${SPDLOG_MASTER_PROJECT})
 | 
			
		||||
option(SPDLOG_FMT_EXTERNAL "Use external fmt library instead of bundled" OFF)
 | 
			
		||||
 | 
			
		||||
if(WIN32)
 | 
			
		||||
	option(SPDLOG_WCHAR_SUPPORT "Support wchar api" OFF)
 | 
			
		||||
    option(SPDLOG_WCHAR_FILENAMES "Support wchar filenames" OFF)    
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
option(SPDLOG_FMT_EXTERNAL_HO "Use external fmt header-only library instead of bundled" OFF)
 | 
			
		||||
option(SPDLOG_NO_EXCEPTIONS "Compile with -fno-exceptions. Call abort() on any spdlog exceptions" OFF)
 | 
			
		||||
 | 
			
		||||
if (SPDLOG_FMT_EXTERNAL AND SPDLOG_FMT_EXTERNAL_HO)
 | 
			
		||||
    message(FATAL_ERROR "SPDLOG_FMT_EXTERNAL and SPDLOG_FMT_EXTERNAL_HO are mutually exclusive")
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
# misc tweakme options
 | 
			
		||||
if(WIN32)
 | 
			
		||||
    option(SPDLOG_WCHAR_SUPPORT "Support wchar api" OFF)
 | 
			
		||||
    option(SPDLOG_WCHAR_FILENAMES "Support wchar filenames" OFF)
 | 
			
		||||
endif()
 | 
			
		||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
 | 
			
		||||
    option(SPDLOG_CLOCK_COARSE "Use the much faster (but much less accurate) CLOCK_REALTIME_COARSE instead of the regular clock," OFF)
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
option(SPDLOG_PREVENT_CHILD_FD "Prevent from child processes to inherit log file descriptors" OFF)
 | 
			
		||||
option(SPDLOG_NO_THREAD_ID "prevent spdlog from querying the thread id on each log call if thread id is not needed" OFF)
 | 
			
		||||
option(SPDLOG_NO_TLS "prevent spdlog from using thread local storage" OFF)
 | 
			
		||||
option(SPDLOG_NO_ATOMIC_LEVELS "prevent spdlog from using of std::atomic log levels (use only if your code never modifies log levels concurrently" OFF)
 | 
			
		||||
 | 
			
		||||
find_package(Threads REQUIRED)
 | 
			
		||||
 | 
			
		||||
message(STATUS "Build type: " ${CMAKE_BUILD_TYPE})
 | 
			
		||||
 | 
			
		||||
#---------------------------------------------------------------------------------------
 | 
			
		||||
# Static/Shared library (shared not supported in windows yet)
 | 
			
		||||
#---------------------------------------------------------------------------------------
 | 
			
		||||
set(SPDLOG_SRCS
 | 
			
		||||
        src/spdlog.cpp
 | 
			
		||||
        src/stdout_sinks.cpp
 | 
			
		||||
        src/fmt.cpp
 | 
			
		||||
        src/color_sinks.cpp
 | 
			
		||||
        src/file_sinks.cpp
 | 
			
		||||
        src/async.cpp)
 | 
			
		||||
 | 
			
		||||
set(SPDLOG_CFLAGS "${PROJECT_NAME}")
 | 
			
		||||
 | 
			
		||||
if(NOT SPDLOG_FMT_EXTERNAL AND NOT SPDLOG_FMT_EXTERNAL_HO)
 | 
			
		||||
    list(APPEND SPDLOG_SRCS src/fmt.cpp)
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
if (SPDLOG_BUILD_SHARED)
 | 
			
		||||
    if(WIN32)
 | 
			
		||||
@@ -123,22 +135,30 @@ target_link_libraries(spdlog_header_only INTERFACE Threads::Threads)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#---------------------------------------------------------------------------------------
 | 
			
		||||
# Use fmt package if using exertnal fmt
 | 
			
		||||
# Use fmt package if using external fmt
 | 
			
		||||
#---------------------------------------------------------------------------------------
 | 
			
		||||
if(SPDLOG_FMT_EXTERNAL)
 | 
			
		||||
if(SPDLOG_FMT_EXTERNAL OR SPDLOG_FMT_EXTERNAL_HO)
 | 
			
		||||
    if (NOT TARGET fmt::fmt)
 | 
			
		||||
        find_package(fmt REQUIRED)
 | 
			
		||||
    endif ()
 | 
			
		||||
 | 
			
		||||
    set(SPDLOG_CFLAGS "${SPDLOG_CFLAGS} -DSPDLOG_FMT_EXTERNAL")
 | 
			
		||||
 | 
			
		||||
    target_compile_definitions(spdlog PUBLIC SPDLOG_FMT_EXTERNAL)
 | 
			
		||||
    target_link_libraries(spdlog PUBLIC fmt::fmt)
 | 
			
		||||
 | 
			
		||||
    target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_FMT_EXTERNAL)
 | 
			
		||||
    target_link_libraries(spdlog_header_only INTERFACE fmt::fmt)
 | 
			
		||||
 | 
			
		||||
    # use external fmt-header-nly
 | 
			
		||||
    if(SPDLOG_FMT_EXTERNAL_HO)
 | 
			
		||||
        target_link_libraries(spdlog PUBLIC fmt::fmt-header-only)
 | 
			
		||||
        target_link_libraries(spdlog_header_only INTERFACE fmt::fmt-header-only)
 | 
			
		||||
    else() # use external compile fmt
 | 
			
		||||
        target_link_libraries(spdlog PUBLIC fmt::fmt)
 | 
			
		||||
        target_link_libraries(spdlog_header_only INTERFACE fmt::fmt)
 | 
			
		||||
    endif()
 | 
			
		||||
 | 
			
		||||
    set(PKG_CONFIG_REQUIRES fmt) # add dependency to pkg-config
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
#---------------------------------------------------------------------------------------
 | 
			
		||||
# Misc definitions according to tweak options
 | 
			
		||||
#---------------------------------------------------------------------------------------
 | 
			
		||||
if(SPDLOG_WCHAR_SUPPORT)
 | 
			
		||||
	target_compile_definitions(spdlog PUBLIC SPDLOG_WCHAR_TO_UTF8_SUPPORT)
 | 
			
		||||
	target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_WCHAR_TO_UTF8_SUPPORT)
 | 
			
		||||
@@ -159,6 +179,31 @@ if(SPDLOG_WCHAR_SUPPORT)
 | 
			
		||||
    endif()
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
if(SPDLOG_CLOCK_COARSE)
 | 
			
		||||
    target_compile_definitions(spdlog PRIVATE SPDLOG_CLOCK_COARSE)
 | 
			
		||||
    target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_CLOCK_COARSE)
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
if(SPDLOG_PREVENT_CHILD_FD)
 | 
			
		||||
    target_compile_definitions(spdlog PRIVATE SPDLOG_PREVENT_CHILD_FD)
 | 
			
		||||
    target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_PREVENT_CHILD_FD)
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
if(SPDLOG_NO_THREAD_ID)
 | 
			
		||||
    target_compile_definitions(spdlog PRIVATE SPDLOG_NO_THREAD_ID)
 | 
			
		||||
    target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_NO_THREAD_ID)
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
if(SPDLOG_NO_TLS)
 | 
			
		||||
    target_compile_definitions(spdlog PRIVATE SPDLOG_NO_TLS)
 | 
			
		||||
    target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_NO_TLS)
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
if(SPDLOG_NO_ATOMIC_LEVELS)
 | 
			
		||||
    target_compile_definitions(spdlog PUBLIC SPDLOG_NO_ATOMIC_LEVELS)
 | 
			
		||||
    target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_NO_ATOMIC_LEVELS)
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#---------------------------------------------------------------------------------------
 | 
			
		||||
# Build binaries
 | 
			
		||||
@@ -188,7 +233,7 @@ if (SPDLOG_INSTALL)
 | 
			
		||||
    set(project_config_out "${CMAKE_CURRENT_BINARY_DIR}/spdlogConfig.cmake")
 | 
			
		||||
    set(config_targets_file "spdlogConfigTargets.cmake")
 | 
			
		||||
    set(version_config_file "${CMAKE_CURRENT_BINARY_DIR}/spdlogConfigVersion.cmake")
 | 
			
		||||
    set(export_dest_dir "${CMAKE_INSTALL_LIBDIR}/spdlog/cmake")
 | 
			
		||||
    set(export_dest_dir "${CMAKE_INSTALL_LIBDIR}/cmake/spdlog")
 | 
			
		||||
    set(pkgconfig_install_dir "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
 | 
			
		||||
    set(pkg_config "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.pc")
 | 
			
		||||
 | 
			
		||||
@@ -196,19 +241,25 @@ if (SPDLOG_INSTALL)
 | 
			
		||||
    # Include files
 | 
			
		||||
    #---------------------------------------------------------------------------------------
 | 
			
		||||
    install(DIRECTORY include/ DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" PATTERN "fmt/bundled" EXCLUDE)
 | 
			
		||||
    install(TARGETS spdlog spdlog_header_only EXPORT spdlog DESTINATION "${CMAKE_INSTALL_LIBDIR}/spdlog")
 | 
			
		||||
    install(TARGETS spdlog spdlog_header_only EXPORT spdlog DESTINATION "${CMAKE_INSTALL_LIBDIR}")
 | 
			
		||||
 | 
			
		||||
    if(NOT SPDLOG_FMT_EXTERNAL)
 | 
			
		||||
    if(NOT SPDLOG_FMT_EXTERNAL AND NOT SPDLOG_FMT_EXTERNAL_HO)
 | 
			
		||||
        install(DIRECTORY include/${PROJECT_NAME}/fmt/bundled/
 | 
			
		||||
                DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}/fmt/bundled/")
 | 
			
		||||
    endif()
 | 
			
		||||
 | 
			
		||||
    #---------------------------------------------------------------------------------------
 | 
			
		||||
    # Package and version files
 | 
			
		||||
    # Install pkg-config file
 | 
			
		||||
    #---------------------------------------------------------------------------------------
 | 
			
		||||
    get_target_property(PKG_CONFIG_DEFINES spdlog INTERFACE_COMPILE_DEFINITIONS)
 | 
			
		||||
    string(REPLACE ";" " -D" PKG_CONFIG_DEFINES "${PKG_CONFIG_DEFINES}")
 | 
			
		||||
    string(CONCAT PKG_CONFIG_DEFINES "-D" "${PKG_CONFIG_DEFINES}")
 | 
			
		||||
    configure_file("cmake/${PROJECT_NAME}.pc.in" "${pkg_config}" @ONLY)
 | 
			
		||||
    install(FILES "${pkg_config}" DESTINATION "${pkgconfig_install_dir}")
 | 
			
		||||
 | 
			
		||||
    #---------------------------------------------------------------------------------------
 | 
			
		||||
    # Install CMake config files
 | 
			
		||||
    #---------------------------------------------------------------------------------------
 | 
			
		||||
    install(EXPORT spdlog
 | 
			
		||||
            DESTINATION ${export_dest_dir}
 | 
			
		||||
            NAMESPACE spdlog::
 | 
			
		||||
@@ -216,6 +267,7 @@ if (SPDLOG_INSTALL)
 | 
			
		||||
 | 
			
		||||
    include(CMakePackageConfigHelpers)
 | 
			
		||||
    configure_file("${project_config_in}" "${project_config_out}" @ONLY)
 | 
			
		||||
 | 
			
		||||
    write_basic_package_version_file("${version_config_file}" COMPATIBILITY SameMajorVersion)
 | 
			
		||||
    install(FILES
 | 
			
		||||
            "${project_config_out}"
 | 
			
		||||
@@ -227,3 +279,4 @@ if (SPDLOG_INSTALL)
 | 
			
		||||
    include(cmake/spdlogCPack.cmake)
 | 
			
		||||
 | 
			
		||||
endif ()
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								third_party/spdlog/README.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								third_party/spdlog/README.md
									
									
									
									
										vendored
									
									
								
							@@ -30,6 +30,8 @@ $ cmake .. && make -j
 | 
			
		||||
* Gentoo: `emerge dev-libs/spdlog`
 | 
			
		||||
* Arch Linux: `yaourt -S spdlog-git`
 | 
			
		||||
* vcpkg: `vcpkg install spdlog`
 | 
			
		||||
* conan: `spdlog/[>=1.4.1]@bincrafters/stable`
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Features
 | 
			
		||||
* Very fast (see [benchmarks](#benchmarks) below).
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										22
									
								
								third_party/spdlog/appveyor.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										22
									
								
								third_party/spdlog/appveyor.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,19 +1,25 @@
 | 
			
		||||
version: 1.0.{build}
 | 
			
		||||
image: Visual Studio 2015
 | 
			
		||||
image: Visual Studio 2017
 | 
			
		||||
environment:
 | 
			
		||||
  matrix:
 | 
			
		||||
  - GENERATOR: '"MinGW Makefiles"'
 | 
			
		||||
    BUILD_TYPE: Debug
 | 
			
		||||
  - GENERATOR: '"MinGW Makefiles"'
 | 
			
		||||
    BUILD_TYPE: Release
 | 
			
		||||
  - GENERATOR: '"Visual Studio 14 2015"'
 | 
			
		||||
    BUILD_TYPE: Debug
 | 
			
		||||
    WCHAR: 'OFF'
 | 
			
		||||
  - GENERATOR: '"Visual Studio 14 2015"'
 | 
			
		||||
    BUILD_TYPE: Release
 | 
			
		||||
    WCHAR: 'ON'
 | 
			
		||||
  - GENERATOR: '"Visual Studio 14 2015 Win64"'
 | 
			
		||||
    BUILD_TYPE: Debug
 | 
			
		||||
    WCHAR: 'ON'
 | 
			
		||||
  - GENERATOR: '"Visual Studio 14 2015 Win64"'
 | 
			
		||||
    BUILD_TYPE: Release
 | 
			
		||||
    WCHAR: 'ON'
 | 
			
		||||
  - GENERATOR: '"Visual Studio 15 2017 Win64"'
 | 
			
		||||
    BUILD_TYPE: Debug
 | 
			
		||||
    WCHAR: 'ON'
 | 
			
		||||
  - GENERATOR: '"Visual Studio 15 2017 Win64"'
 | 
			
		||||
    BUILD_TYPE: Release
 | 
			
		||||
    WCHAR: 'OFf'
 | 
			
		||||
build_script:
 | 
			
		||||
- cmd: >-
 | 
			
		||||
    set
 | 
			
		||||
@@ -22,11 +28,9 @@ build_script:
 | 
			
		||||
    
 | 
			
		||||
    cd build 
 | 
			
		||||
 | 
			
		||||
    set PATH=%PATH:C:\Program Files\Git\usr\bin;=%
 | 
			
		||||
    set PATH=%PATH%:C:\Program Files\Git\usr\bin
 | 
			
		||||
    
 | 
			
		||||
    set PATH=C:\mingw-w64\i686-5.3.0-posix-dwarf-rt_v4-rev0\mingw32\bin;%PATH%
 | 
			
		||||
 | 
			
		||||
    cmake .. -G %GENERATOR% -DCMAKE_BUILD_TYPE=%BUILD_TYPE% -DSPDLOG_WCHAR_SUPPORT=ON -DSPDLOG_BUILD_EXAMPLE=ON -DSPDLOG_BUILD_EXAMPLE_HO=ON -DSPDLOG_BUILD_TESTS=ON -DSPDLOG_BUILD_TESTS_HO=OFF
 | 
			
		||||
    cmake .. -G %GENERATOR% -DCMAKE_BUILD_TYPE=%BUILD_TYPE% -DSPDLOG_WCHAR_SUPPORT=%WCHAR% -DSPDLOG_BUILD_EXAMPLE=ON -DSPDLOG_BUILD_EXAMPLE_HO=ON -DSPDLOG_BUILD_TESTS=ON -DSPDLOG_BUILD_TESTS_HO=OFF
 | 
			
		||||
 | 
			
		||||
    cmake --build . --config %BUILD_TYPE%
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								third_party/spdlog/bench/CMakeLists.txt
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								third_party/spdlog/bench/CMakeLists.txt
									
									
									
									
										vendored
									
									
								
							@@ -24,5 +24,3 @@ target_link_libraries(latency PRIVATE benchmark::benchmark spdlog::spdlog)
 | 
			
		||||
 | 
			
		||||
add_executable(formatter-bench formatter-bench.cpp)
 | 
			
		||||
target_link_libraries(formatter-bench PRIVATE benchmark::benchmark spdlog::spdlog)
 | 
			
		||||
 | 
			
		||||
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/logs")
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										22
									
								
								third_party/spdlog/bench/latency.cpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										22
									
								
								third_party/spdlog/bench/latency.cpp
									
									
									
									
										vendored
									
									
								
							@@ -16,26 +16,6 @@
 | 
			
		||||
#include "spdlog/sinks/null_sink.h"
 | 
			
		||||
#include "spdlog/sinks/rotating_file_sink.h"
 | 
			
		||||
 | 
			
		||||
void prepare_logdir()
 | 
			
		||||
{
 | 
			
		||||
    spdlog::info("Preparing latency_logs directory..");
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
    system("if not exist logs mkdir latency_logs");
 | 
			
		||||
    system("del /F /Q logs\\*");
 | 
			
		||||
#else
 | 
			
		||||
    auto rv = system("mkdir -p latency_logs");
 | 
			
		||||
    if (rv != 0)
 | 
			
		||||
    {
 | 
			
		||||
        throw std::runtime_error("Failed to mkdir -p latency_logs");
 | 
			
		||||
    }
 | 
			
		||||
    rv = system("rm -f latency_logs/*");
 | 
			
		||||
    if (rv != 0)
 | 
			
		||||
    {
 | 
			
		||||
        throw std::runtime_error("Failed to rm -f latency_logs/*");
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void bench_c_string(benchmark::State &state, std::shared_ptr<spdlog::logger> logger)
 | 
			
		||||
{
 | 
			
		||||
    const char *msg = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum pharetra metus cursus "
 | 
			
		||||
@@ -83,8 +63,6 @@ int main(int argc, char *argv[])
 | 
			
		||||
    size_t rotating_files = 5;
 | 
			
		||||
    int n_threads = benchmark::CPUInfo::Get().num_cpus;
 | 
			
		||||
 | 
			
		||||
    prepare_logdir();
 | 
			
		||||
 | 
			
		||||
    // disabled loggers
 | 
			
		||||
    auto disabled_logger = std::make_shared<spdlog::logger>("bench", std::make_shared<null_sink_mt>());
 | 
			
		||||
    disabled_logger->set_level(spdlog::level::off);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								third_party/spdlog/bench/meson.build
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								third_party/spdlog/bench/meson.build
									
									
									
									
										vendored
									
									
								
							@@ -12,4 +12,3 @@ foreach i : bench_matrix
 | 
			
		||||
  benchmark('bench_' + i[0], bench_exe, args: i[2])
 | 
			
		||||
endforeach
 | 
			
		||||
 | 
			
		||||
run_command(find_program('mkdir'), meson.current_build_dir() + '/logs')
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										6
									
								
								third_party/spdlog/cmake/spdlog.pc.in
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								third_party/spdlog/cmake/spdlog.pc.in
									
									
									
									
										vendored
									
									
								
							@@ -7,5 +7,7 @@ Name: lib@PROJECT_NAME@
 | 
			
		||||
Description: Fast C++ logging library.
 | 
			
		||||
URL: https://github.com/gabime/@PROJECT_NAME@
 | 
			
		||||
Version: @SPDLOG_VERSION@
 | 
			
		||||
CFlags: -I${includedir}/@SPDLOG_CFLAGS@
 | 
			
		||||
Libs: -L${libdir}/@PROJECT_NAME@ -l@PROJECT_NAME@
 | 
			
		||||
CFlags: -I${includedir} @PKG_CONFIG_DEFINES@
 | 
			
		||||
Libs: -L${libdir} -lspdlog -pthread
 | 
			
		||||
Requires: @PKG_CONFIG_REQUIRES@
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										5
									
								
								third_party/spdlog/cmake/utils.cmake
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								third_party/spdlog/cmake/utils.cmake
									
									
									
									
										vendored
									
									
								
							@@ -29,7 +29,10 @@ function(spdlog_enable_warnings target_name)
 | 
			
		||||
    target_compile_options(${target_name} PRIVATE
 | 
			
		||||
        $<$<OR:$<CXX_COMPILER_ID:Clang>,$<CXX_COMPILER_ID:AppleClang>,$<CXX_COMPILER_ID:GNU>>:
 | 
			
		||||
            -Wall -Wextra -Wconversion -pedantic -Wfatal-errors>
 | 
			
		||||
        $<$<CXX_COMPILER_ID:MSVC>:/W4 /WX>)
 | 
			
		||||
        $<$<CXX_COMPILER_ID:MSVC>:/W4>)
 | 
			
		||||
		if(MSVC_VERSION GREATER_EQUAL  1910)  #Allow non fatal security wanrnings for msvc 2015 
 | 
			
		||||
			target_compile_options(${target_name} PRIVATE /WX)
 | 
			
		||||
		endif()
 | 
			
		||||
endfunction()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								third_party/spdlog/example/CMakeLists.txt
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								third_party/spdlog/example/CMakeLists.txt
									
									
									
									
										vendored
									
									
								
							@@ -25,5 +25,3 @@ if(SPDLOG_BUILD_EXAMPLE_HO)
 | 
			
		||||
    target_link_libraries(example_header_only PRIVATE spdlog::spdlog_header_only)
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
# Create logs directory
 | 
			
		||||
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/logs")
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								third_party/spdlog/example/example.cpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								third_party/spdlog/example/example.cpp
									
									
									
									
										vendored
									
									
								
							@@ -143,7 +143,7 @@ void async_example()
 | 
			
		||||
#include "spdlog/fmt/bin_to_hex.h"
 | 
			
		||||
void binary_example()
 | 
			
		||||
{
 | 
			
		||||
    std::vector<char> buf;
 | 
			
		||||
    std::vector<char> buf(80);
 | 
			
		||||
    for (int i = 0; i < 80; i++)
 | 
			
		||||
    {
 | 
			
		||||
        buf.push_back(static_cast<char>(i & 0xff));
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								third_party/spdlog/example/meson.build
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								third_party/spdlog/example/meson.build
									
									
									
									
										vendored
									
									
								
							@@ -1,5 +1,4 @@
 | 
			
		||||
executable('example', 'example.cpp', dependencies: spdlog_dep)
 | 
			
		||||
executable('example_header_only', 'example.cpp', dependencies: spdlog_headeronly_dep)
 | 
			
		||||
run_command(find_program('mkdir'), meson.current_build_dir() + '/logs')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										8
									
								
								third_party/spdlog/include/spdlog/async.h
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								third_party/spdlog/include/spdlog/async.h
									
									
									
									
										vendored
									
									
								
							@@ -6,7 +6,7 @@
 | 
			
		||||
//
 | 
			
		||||
// Async logging using global thread pool
 | 
			
		||||
// All loggers created here share same global thread pool.
 | 
			
		||||
// Each log message is pushed to a queue along withe a shared pointer to the
 | 
			
		||||
// 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
 | 
			
		||||
@@ -14,9 +14,9 @@
 | 
			
		||||
// 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 <spdlog/async_logger.h>
 | 
			
		||||
#include <spdlog/details/registry.h>
 | 
			
		||||
#include <spdlog/details/thread_pool.h>
 | 
			
		||||
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <mutex>
 | 
			
		||||
 
 | 
			
		||||
@@ -4,11 +4,11 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#ifndef SPDLOG_HEADER_ONLY
 | 
			
		||||
#include "spdlog/async_logger.h"
 | 
			
		||||
#include <spdlog/async_logger.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include "spdlog/sinks/sink.h"
 | 
			
		||||
#include "spdlog/details/thread_pool.h"
 | 
			
		||||
#include <spdlog/sinks/sink.h>
 | 
			
		||||
#include <spdlog/details/thread_pool.h>
 | 
			
		||||
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <string>
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,7 @@
 | 
			
		||||
// Upon destruction, logs all remaining messages in the queue before
 | 
			
		||||
// destructing..
 | 
			
		||||
 | 
			
		||||
#include "spdlog/logger.h"
 | 
			
		||||
#include <spdlog/logger.h>
 | 
			
		||||
 | 
			
		||||
namespace spdlog {
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#ifndef SPDLOG_HEADER_ONLY
 | 
			
		||||
#include "spdlog/common.h"
 | 
			
		||||
#include <spdlog/common.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
namespace spdlog {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										8
									
								
								third_party/spdlog/include/spdlog/common.h
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								third_party/spdlog/include/spdlog/common.h
									
									
									
									
										vendored
									
									
								
							@@ -3,8 +3,8 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "spdlog/tweakme.h"
 | 
			
		||||
#include "spdlog/details/null_mutex.h"
 | 
			
		||||
#include <spdlog/tweakme.h>
 | 
			
		||||
#include <spdlog/details/null_mutex.h>
 | 
			
		||||
 | 
			
		||||
#include <atomic>
 | 
			
		||||
#include <chrono>
 | 
			
		||||
@@ -35,7 +35,7 @@
 | 
			
		||||
#define SPDLOG_INLINE inline
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include "spdlog/fmt/fmt.h"
 | 
			
		||||
#include <spdlog/fmt/fmt.h>
 | 
			
		||||
 | 
			
		||||
// visual studio upto 2013 does not support noexcept nor constexpr
 | 
			
		||||
#if defined(_MSC_VER) && (_MSC_VER < 1900)
 | 
			
		||||
@@ -62,7 +62,7 @@
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifndef SPDLOG_FUNCTION
 | 
			
		||||
#define SPDLOG_FUNCTION __FUNCTION__
 | 
			
		||||
#define SPDLOG_FUNCTION static_cast<const char *>(__FUNCTION__)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef SPDLOG_NO_EXCEPTIONS
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#ifndef SPDLOG_HEADER_ONLY
 | 
			
		||||
#include "spdlog/details/backtracer.h"
 | 
			
		||||
#include <spdlog/details/backtracer.h>
 | 
			
		||||
#endif
 | 
			
		||||
namespace spdlog {
 | 
			
		||||
namespace details {
 | 
			
		||||
@@ -26,7 +26,7 @@ SPDLOG_INLINE backtracer &backtracer::operator=(backtracer other)
 | 
			
		||||
{
 | 
			
		||||
    std::lock_guard<std::mutex> lock(mutex_);
 | 
			
		||||
    enabled_ = other.enabled();
 | 
			
		||||
    messages_ = other.messages_;
 | 
			
		||||
    messages_ = std::move(other.messages_);
 | 
			
		||||
    return *this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -48,11 +48,6 @@ SPDLOG_INLINE bool backtracer::enabled() const
 | 
			
		||||
    return enabled_.load(std::memory_order_relaxed);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
SPDLOG_INLINE backtracer::operator bool() const
 | 
			
		||||
{
 | 
			
		||||
    return enabled();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
SPDLOG_INLINE void backtracer::push_back(const log_msg &msg)
 | 
			
		||||
{
 | 
			
		||||
    std::lock_guard<std::mutex> lock{mutex_};
 | 
			
		||||
 
 | 
			
		||||
@@ -3,8 +3,8 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "spdlog/details/log_msg_buffer.h"
 | 
			
		||||
#include "spdlog/details/circular_q.h"
 | 
			
		||||
#include <spdlog/details/log_msg_buffer.h>
 | 
			
		||||
#include <spdlog/details/circular_q.h>
 | 
			
		||||
 | 
			
		||||
#include <atomic>
 | 
			
		||||
#include <mutex>
 | 
			
		||||
@@ -31,7 +31,6 @@ public:
 | 
			
		||||
    void enable(size_t size);
 | 
			
		||||
    void disable();
 | 
			
		||||
    bool enabled() const;
 | 
			
		||||
    explicit operator bool() const;
 | 
			
		||||
    void push_back(const log_msg &msg);
 | 
			
		||||
 | 
			
		||||
    // pop all items in the q and apply the given fun on each of them.
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,11 @@
 | 
			
		||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
 | 
			
		||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
 | 
			
		||||
 | 
			
		||||
// cirucal q view of std::vector.
 | 
			
		||||
// circular q view of std::vector.
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <cassert>
 | 
			
		||||
 | 
			
		||||
namespace spdlog {
 | 
			
		||||
namespace details {
 | 
			
		||||
@@ -72,6 +73,27 @@ public:
 | 
			
		||||
        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()
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "spdlog/details/null_mutex.h"
 | 
			
		||||
#include <spdlog/details/null_mutex.h>
 | 
			
		||||
#include <mutex>
 | 
			
		||||
 | 
			
		||||
namespace spdlog {
 | 
			
		||||
 
 | 
			
		||||
@@ -4,11 +4,11 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#ifndef SPDLOG_HEADER_ONLY
 | 
			
		||||
#include "spdlog/details/file_helper.h"
 | 
			
		||||
#include <spdlog/details/file_helper.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include "spdlog/details/os.h"
 | 
			
		||||
#include "spdlog/common.h"
 | 
			
		||||
#include <spdlog/details/os.h>
 | 
			
		||||
#include <spdlog/common.h>
 | 
			
		||||
 | 
			
		||||
#include <cerrno>
 | 
			
		||||
#include <chrono>
 | 
			
		||||
@@ -28,28 +28,31 @@ SPDLOG_INLINE file_helper::~file_helper()
 | 
			
		||||
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");
 | 
			
		||||
    _filename = fname;
 | 
			
		||||
    for (int tries = 0; tries < open_tries; ++tries)
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
        details::os::sleep_for_millis(open_interval_);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    SPDLOG_THROW(spdlog_ex("Failed opening file " + os::filename_to_str(_filename) + " for writing", errno));
 | 
			
		||||
    SPDLOG_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())
 | 
			
		||||
    if (filename_.empty())
 | 
			
		||||
    {
 | 
			
		||||
        SPDLOG_THROW(spdlog_ex("Failed re opening file - was not opened before"));
 | 
			
		||||
    }
 | 
			
		||||
    open(_filename, truncate);
 | 
			
		||||
    this->open(filename_, truncate);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
SPDLOG_INLINE void file_helper::flush()
 | 
			
		||||
@@ -72,7 +75,7 @@ SPDLOG_INLINE void file_helper::write(const memory_buf_t &buf)
 | 
			
		||||
    auto data = buf.data();
 | 
			
		||||
    if (std::fwrite(data, 1, msg_size, fd_) != msg_size)
 | 
			
		||||
    {
 | 
			
		||||
        SPDLOG_THROW(spdlog_ex("Failed writing to file " + os::filename_to_str(_filename), errno));
 | 
			
		||||
        SPDLOG_THROW(spdlog_ex("Failed writing to file " + os::filename_to_str(filename_), errno));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -80,19 +83,14 @@ SPDLOG_INLINE size_t file_helper::size() const
 | 
			
		||||
{
 | 
			
		||||
    if (fd_ == nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        SPDLOG_THROW(spdlog_ex("Cannot use size() on closed file " + os::filename_to_str(_filename)));
 | 
			
		||||
        SPDLOG_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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
SPDLOG_INLINE bool file_helper::file_exists(const filename_t &fname)
 | 
			
		||||
{
 | 
			
		||||
    return os::file_exists(fname);
 | 
			
		||||
    return filename_;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
@@ -119,7 +117,7 @@ SPDLOG_INLINE std::tuple<filename_t, filename_t> file_helper::split_by_extension
 | 
			
		||||
        return std::make_tuple(fname, filename_t());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // treat casese like "/etc/rc.d/somelogfile or "/abc/.hiddenfile"
 | 
			
		||||
    // 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)
 | 
			
		||||
    {
 | 
			
		||||
@@ -129,5 +127,6 @@ SPDLOG_INLINE std::tuple<filename_t, filename_t> file_helper::split_by_extension
 | 
			
		||||
    // 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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "spdlog/common.h"
 | 
			
		||||
#include <spdlog/common.h>
 | 
			
		||||
#include <tuple>
 | 
			
		||||
 | 
			
		||||
namespace spdlog {
 | 
			
		||||
@@ -29,7 +29,6 @@ public:
 | 
			
		||||
    void write(const memory_buf_t &buf);
 | 
			
		||||
    size_t size() const;
 | 
			
		||||
    const filename_t &filename() const;
 | 
			
		||||
    static bool file_exists(const filename_t &fname);
 | 
			
		||||
 | 
			
		||||
    //
 | 
			
		||||
    // return file path and its extension:
 | 
			
		||||
@@ -47,10 +46,10 @@ public:
 | 
			
		||||
    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;
 | 
			
		||||
    const int open_tries_ = 5;
 | 
			
		||||
    const int open_interval_ = 10;
 | 
			
		||||
    std::FILE *fd_{nullptr};
 | 
			
		||||
    filename_t _filename;
 | 
			
		||||
    filename_t filename_;
 | 
			
		||||
};
 | 
			
		||||
} // namespace details
 | 
			
		||||
} // namespace spdlog
 | 
			
		||||
 
 | 
			
		||||
@@ -4,8 +4,8 @@
 | 
			
		||||
 | 
			
		||||
#include <chrono>
 | 
			
		||||
#include <type_traits>
 | 
			
		||||
#include "spdlog/fmt/fmt.h"
 | 
			
		||||
#include "spdlog/common.h"
 | 
			
		||||
#include <spdlog/fmt/fmt.h>
 | 
			
		||||
#include <spdlog/common.h>
 | 
			
		||||
 | 
			
		||||
// Some fmt helpers to efficiently format and pad ints and strings
 | 
			
		||||
namespace spdlog {
 | 
			
		||||
 
 | 
			
		||||
@@ -4,21 +4,19 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#ifndef SPDLOG_HEADER_ONLY
 | 
			
		||||
#include "spdlog/details/log_msg.h"
 | 
			
		||||
#include <spdlog/details/log_msg.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include "spdlog/details/os.h"
 | 
			
		||||
#include <spdlog/details/os.h>
 | 
			
		||||
 | 
			
		||||
namespace spdlog {
 | 
			
		||||
namespace details {
 | 
			
		||||
 | 
			
		||||
SPDLOG_INLINE log_msg::log_msg(spdlog::source_loc loc, string_view_t logger_name, spdlog::level::level_enum lvl, spdlog::string_view_t msg)
 | 
			
		||||
    : logger_name(logger_name)
 | 
			
		||||
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)
 | 
			
		||||
    : logger_name(a_logger_name)
 | 
			
		||||
    , level(lvl)
 | 
			
		||||
#ifndef SPDLOG_NO_DATETIME
 | 
			
		||||
    , time(os::now())
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifndef SPDLOG_NO_THREAD_ID
 | 
			
		||||
    , thread_id(os::thread_id())
 | 
			
		||||
#endif
 | 
			
		||||
@@ -26,8 +24,8 @@ SPDLOG_INLINE log_msg::log_msg(spdlog::source_loc loc, string_view_t logger_name
 | 
			
		||||
    , payload(msg)
 | 
			
		||||
{}
 | 
			
		||||
 | 
			
		||||
SPDLOG_INLINE log_msg::log_msg(string_view_t logger_name, spdlog::level::level_enum lvl, spdlog::string_view_t msg)
 | 
			
		||||
    : log_msg(source_loc{}, 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(source_loc{}, a_logger_name, lvl, msg)
 | 
			
		||||
{}
 | 
			
		||||
 | 
			
		||||
} // namespace details
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "spdlog/common.h"
 | 
			
		||||
#include <spdlog/common.h>
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
namespace spdlog {
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#ifndef SPDLOG_HEADER_ONLY
 | 
			
		||||
#include "spdlog/details/log_msg_buffer.h"
 | 
			
		||||
#include <spdlog/details/log_msg_buffer.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
namespace spdlog {
 | 
			
		||||
@@ -26,9 +26,7 @@ SPDLOG_INLINE log_msg_buffer::log_msg_buffer(const log_msg_buffer &other)
 | 
			
		||||
    update_string_views();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
SPDLOG_INLINE log_msg_buffer::log_msg_buffer(log_msg_buffer &&other)
 | 
			
		||||
    : log_msg{std::move(other)}
 | 
			
		||||
    , buffer{std::move(other.buffer)}
 | 
			
		||||
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();
 | 
			
		||||
}
 | 
			
		||||
@@ -42,9 +40,9 @@ SPDLOG_INLINE log_msg_buffer &log_msg_buffer::operator=(const log_msg_buffer &ot
 | 
			
		||||
    return *this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
SPDLOG_INLINE log_msg_buffer &log_msg_buffer::operator=(log_msg_buffer &&other)
 | 
			
		||||
SPDLOG_INLINE log_msg_buffer &log_msg_buffer::operator=(log_msg_buffer &&other) SPDLOG_NOEXCEPT
 | 
			
		||||
{
 | 
			
		||||
    log_msg::operator=(std::move(other));
 | 
			
		||||
    log_msg::operator=(other);
 | 
			
		||||
    buffer = std::move(other.buffer);
 | 
			
		||||
    update_string_views();
 | 
			
		||||
    return *this;
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "spdlog/details/log_msg.h"
 | 
			
		||||
#include <spdlog/details/log_msg.h>
 | 
			
		||||
 | 
			
		||||
namespace spdlog {
 | 
			
		||||
namespace details {
 | 
			
		||||
@@ -20,9 +20,9 @@ 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);
 | 
			
		||||
    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);
 | 
			
		||||
    log_msg_buffer &operator=(log_msg_buffer &&other) SPDLOG_NOEXCEPT;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace details
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,7 @@
 | 
			
		||||
// dequeue_for(..) - will block until the queue is not empty or timeout have
 | 
			
		||||
// passed.
 | 
			
		||||
 | 
			
		||||
#include "spdlog/details/circular_q.h"
 | 
			
		||||
#include <spdlog/details/circular_q.h>
 | 
			
		||||
 | 
			
		||||
#include <condition_variable>
 | 
			
		||||
#include <mutex>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										158
									
								
								third_party/spdlog/include/spdlog/details/os-inl.h
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										158
									
								
								third_party/spdlog/include/spdlog/details/os-inl.h
									
									
									
									
										vendored
									
									
								
							@@ -4,10 +4,10 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#ifndef SPDLOG_HEADER_ONLY
 | 
			
		||||
#include "spdlog/details/os.h"
 | 
			
		||||
#include <spdlog/details/os.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include "spdlog/common.h"
 | 
			
		||||
#include <spdlog/common.h>
 | 
			
		||||
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <chrono>
 | 
			
		||||
@@ -42,6 +42,8 @@
 | 
			
		||||
#include <limits>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <direct.h> // for _mkdir/_wmkdir
 | 
			
		||||
 | 
			
		||||
#else // unix
 | 
			
		||||
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
@@ -91,17 +93,17 @@ SPDLOG_INLINE std::tm localtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT
 | 
			
		||||
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
    std::tm tm;
 | 
			
		||||
    localtime_s(&tm, &time_tt);
 | 
			
		||||
    ::localtime_s(&tm, &time_tt);
 | 
			
		||||
#else
 | 
			
		||||
    std::tm tm;
 | 
			
		||||
    localtime_r(&time_tt, &tm);
 | 
			
		||||
    ::localtime_r(&time_tt, &tm);
 | 
			
		||||
#endif
 | 
			
		||||
    return tm;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
SPDLOG_INLINE std::tm localtime() SPDLOG_NOEXCEPT
 | 
			
		||||
{
 | 
			
		||||
    std::time_t now_t = time(nullptr);
 | 
			
		||||
    std::time_t now_t = ::time(nullptr);
 | 
			
		||||
    return localtime(now_t);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -110,52 +112,52 @@ SPDLOG_INLINE std::tm gmtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT
 | 
			
		||||
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
    std::tm tm;
 | 
			
		||||
    gmtime_s(&tm, &time_tt);
 | 
			
		||||
    ::gmtime_s(&tm, &time_tt);
 | 
			
		||||
#else
 | 
			
		||||
    std::tm tm;
 | 
			
		||||
    gmtime_r(&time_tt, &tm);
 | 
			
		||||
    ::gmtime_r(&time_tt, &tm);
 | 
			
		||||
#endif
 | 
			
		||||
    return tm;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
SPDLOG_INLINE std::tm gmtime() SPDLOG_NOEXCEPT
 | 
			
		||||
{
 | 
			
		||||
    std::time_t now_t = time(nullptr);
 | 
			
		||||
    std::time_t now_t = ::time(nullptr);
 | 
			
		||||
    return gmtime(now_t);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef SPDLOG_PREVENT_CHILD_FD
 | 
			
		||||
SPDLOG_INLINE void prevent_child_fd(FILE *f)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
#if !defined(__cplusplus_winrt)
 | 
			
		||||
    auto file_handle = reinterpret_cast<HANDLE>(_get_osfhandle(_fileno(f)));
 | 
			
		||||
    auto file_handle = reinterpret_cast<HANDLE>(_get_osfhandle(::_fileno(f)));
 | 
			
		||||
    if (!::SetHandleInformation(file_handle, HANDLE_FLAG_INHERIT, 0))
 | 
			
		||||
        SPDLOG_THROW(spdlog_ex("SetHandleInformation failed", errno));
 | 
			
		||||
#endif
 | 
			
		||||
#else
 | 
			
		||||
    auto fd = fileno(f);
 | 
			
		||||
    if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
 | 
			
		||||
    auto fd = ::fileno(f);
 | 
			
		||||
    if (::fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
 | 
			
		||||
    {
 | 
			
		||||
        SPDLOG_THROW(spdlog_ex("fcntl with FD_CLOEXEC failed", errno));
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
#endif // SPDLOG_PREVENT_CHILD_FD
 | 
			
		||||
 | 
			
		||||
// 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);
 | 
			
		||||
    *fp = ::_wfsopen((filename.c_str()), mode.c_str(), _SH_DENYNO);
 | 
			
		||||
#else
 | 
			
		||||
    *fp = _fsopen((filename.c_str()), mode.c_str(), _SH_DENYNO);
 | 
			
		||||
    *fp = ::_fsopen((filename.c_str()), mode.c_str(), _SH_DENYNO);
 | 
			
		||||
#endif
 | 
			
		||||
#else // unix
 | 
			
		||||
    *fp = fopen((filename.c_str()), mode.c_str());
 | 
			
		||||
    *fp = ::fopen((filename.c_str()), mode.c_str());
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef SPDLOG_PREVENT_CHILD_FD
 | 
			
		||||
    //  prevent child processes from inheriting log file descriptors
 | 
			
		||||
    if (*fp != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        prevent_child_fd(*fp);
 | 
			
		||||
@@ -167,7 +169,7 @@ SPDLOG_INLINE bool fopen_s(FILE **fp, const filename_t &filename, const filename
 | 
			
		||||
SPDLOG_INLINE int remove(const filename_t &filename) SPDLOG_NOEXCEPT
 | 
			
		||||
{
 | 
			
		||||
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
 | 
			
		||||
    return _wremove(filename.c_str());
 | 
			
		||||
    return ::_wremove(filename.c_str());
 | 
			
		||||
#else
 | 
			
		||||
    return std::remove(filename.c_str());
 | 
			
		||||
#endif
 | 
			
		||||
@@ -175,28 +177,28 @@ SPDLOG_INLINE int remove(const filename_t &filename) SPDLOG_NOEXCEPT
 | 
			
		||||
 | 
			
		||||
SPDLOG_INLINE int remove_if_exists(const filename_t &filename) SPDLOG_NOEXCEPT
 | 
			
		||||
{
 | 
			
		||||
    return file_exists(filename) ? remove(filename) : 0;
 | 
			
		||||
    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());
 | 
			
		||||
    return ::_wrename(filename1.c_str(), filename2.c_str());
 | 
			
		||||
#else
 | 
			
		||||
    return std::rename(filename1.c_str(), filename2.c_str());
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Return true if file exists
 | 
			
		||||
SPDLOG_INLINE bool file_exists(const filename_t &filename) SPDLOG_NOEXCEPT
 | 
			
		||||
// 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());
 | 
			
		||||
    auto attribs = ::GetFileAttributesW(filename.c_str());
 | 
			
		||||
#else
 | 
			
		||||
    auto attribs = GetFileAttributesA(filename.c_str());
 | 
			
		||||
    auto attribs = ::GetFileAttributesA(filename.c_str());
 | 
			
		||||
#endif
 | 
			
		||||
    return (attribs != INVALID_FILE_ATTRIBUTES && !(attribs & FILE_ATTRIBUTE_DIRECTORY));
 | 
			
		||||
    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);
 | 
			
		||||
@@ -211,16 +213,16 @@ SPDLOG_INLINE size_t filesize(FILE *f)
 | 
			
		||||
        SPDLOG_THROW(spdlog_ex("Failed getting file size. fd is null"));
 | 
			
		||||
    }
 | 
			
		||||
#if defined(_WIN32) && !defined(__CYGWIN__)
 | 
			
		||||
    int fd = _fileno(f);
 | 
			
		||||
    int fd = ::_fileno(f);
 | 
			
		||||
#if _WIN64 // 64 bits
 | 
			
		||||
    __int64 ret = _filelengthi64(fd);
 | 
			
		||||
    __int64 ret = ::_filelengthi64(fd);
 | 
			
		||||
    if (ret >= 0)
 | 
			
		||||
    {
 | 
			
		||||
        return static_cast<size_t>(ret);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#else // windows 32 bits
 | 
			
		||||
    long ret = _filelength(fd);
 | 
			
		||||
    long ret = ::_filelength(fd);
 | 
			
		||||
    if (ret >= 0)
 | 
			
		||||
    {
 | 
			
		||||
        return static_cast<size_t>(ret);
 | 
			
		||||
@@ -228,7 +230,12 @@ SPDLOG_INLINE size_t filesize(FILE *f)
 | 
			
		||||
#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;
 | 
			
		||||
@@ -236,9 +243,8 @@ SPDLOG_INLINE size_t filesize(FILE *f)
 | 
			
		||||
    {
 | 
			
		||||
        return static_cast<size_t>(st.st_size);
 | 
			
		||||
    }
 | 
			
		||||
#else // unix 32 bits or cygwin
 | 
			
		||||
#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);
 | 
			
		||||
@@ -255,10 +261,10 @@ 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);
 | 
			
		||||
    auto rv = ::GetTimeZoneInformation(&tzinfo);
 | 
			
		||||
#else
 | 
			
		||||
    DYNAMIC_TIME_ZONE_INFORMATION tzinfo;
 | 
			
		||||
    auto rv = GetDynamicTimeZoneInformation(&tzinfo);
 | 
			
		||||
    auto rv = ::GetDynamicTimeZoneInformation(&tzinfo);
 | 
			
		||||
#endif
 | 
			
		||||
    if (rv == TIME_ZONE_ID_INVALID)
 | 
			
		||||
        SPDLOG_THROW(spdlog::spdlog_ex("Failed getting timezone info. ", errno));
 | 
			
		||||
@@ -275,7 +281,7 @@ SPDLOG_INLINE int utc_minutes_offset(const std::tm &tm)
 | 
			
		||||
    return offset;
 | 
			
		||||
#else
 | 
			
		||||
 | 
			
		||||
#if defined(sun) || defined(__sun) || defined(_AIX)
 | 
			
		||||
#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
 | 
			
		||||
    {
 | 
			
		||||
@@ -324,15 +330,15 @@ SPDLOG_INLINE size_t _thread_id() SPDLOG_NOEXCEPT
 | 
			
		||||
#if defined(__ANDROID__) && defined(__ANDROID_API__) && (__ANDROID_API__ < 21)
 | 
			
		||||
#define SYS_gettid __NR_gettid
 | 
			
		||||
#endif
 | 
			
		||||
    return static_cast<size_t>(syscall(SYS_gettid));
 | 
			
		||||
    return static_cast<size_t>(::syscall(SYS_gettid));
 | 
			
		||||
#elif defined(_AIX) || defined(__DragonFly__) || defined(__FreeBSD__)
 | 
			
		||||
    return static_cast<size_t>(pthread_getthreadid_np());
 | 
			
		||||
    return static_cast<size_t>(::pthread_getthreadid_np());
 | 
			
		||||
#elif defined(__NetBSD__)
 | 
			
		||||
    return static_cast<size_t>(_lwp_self());
 | 
			
		||||
    return static_cast<size_t>(::_lwp_self());
 | 
			
		||||
#elif defined(__OpenBSD__)
 | 
			
		||||
    return static_cast<size_t>(getthrid());
 | 
			
		||||
    return static_cast<size_t>(::getthrid());
 | 
			
		||||
#elif defined(__sun)
 | 
			
		||||
    return static_cast<size_t>(thr_self());
 | 
			
		||||
    return static_cast<size_t>(::thr_self());
 | 
			
		||||
#elif __APPLE__
 | 
			
		||||
    uint64_t tid;
 | 
			
		||||
    pthread_threadid_np(nullptr, &tid);
 | 
			
		||||
@@ -417,16 +423,16 @@ SPDLOG_INLINE bool in_terminal(FILE *file) SPDLOG_NOEXCEPT
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
    return _isatty(_fileno(file)) != 0;
 | 
			
		||||
    return ::_isatty(_fileno(file)) != 0;
 | 
			
		||||
#else
 | 
			
		||||
    return isatty(fileno(file)) != 0;
 | 
			
		||||
    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()))
 | 
			
		||||
    if (wstr.size() > static_cast<size_t>((std::numeric_limits<int>::max)()))
 | 
			
		||||
    {
 | 
			
		||||
        SPDLOG_THROW(spdlog::spdlog_ex("UTF-16 string is too big to be converted to UTF-8"));
 | 
			
		||||
    }
 | 
			
		||||
@@ -460,6 +466,76 @@ SPDLOG_INLINE void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target)
 | 
			
		||||
}
 | 
			
		||||
#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{};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace os
 | 
			
		||||
} // namespace details
 | 
			
		||||
} // namespace spdlog
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										21
									
								
								third_party/spdlog/include/spdlog/details/os.h
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										21
									
								
								third_party/spdlog/include/spdlog/details/os.h
									
									
									
									
										vendored
									
									
								
							@@ -3,7 +3,7 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "spdlog/common.h"
 | 
			
		||||
#include <spdlog/common.h>
 | 
			
		||||
#include <ctime> // std::time_t
 | 
			
		||||
 | 
			
		||||
namespace spdlog {
 | 
			
		||||
@@ -33,12 +33,14 @@ SPDLOG_CONSTEXPR static const char *default_eol = SPDLOG_EOL;
 | 
			
		||||
 | 
			
		||||
// folder separator
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
const char folder_sep = '\\';
 | 
			
		||||
static const char folder_sep = '\\';
 | 
			
		||||
#else
 | 
			
		||||
SPDLOG_CONSTEXPR static const char folder_sep = '/';
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef SPDLOG_PREVENT_CHILD_FD
 | 
			
		||||
void prevent_child_fd(FILE *f);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
// fopen_s on non windows for writing
 | 
			
		||||
bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode);
 | 
			
		||||
@@ -53,7 +55,7 @@ int remove_if_exists(const filename_t &filename) SPDLOG_NOEXCEPT;
 | 
			
		||||
int rename(const filename_t &filename1, const filename_t &filename2) SPDLOG_NOEXCEPT;
 | 
			
		||||
 | 
			
		||||
// Return if file exists.
 | 
			
		||||
bool file_exists(const filename_t &filename) SPDLOG_NOEXCEPT;
 | 
			
		||||
bool path_exists(const filename_t &filename) SPDLOG_NOEXCEPT;
 | 
			
		||||
 | 
			
		||||
// Return file size according to open FILE* object
 | 
			
		||||
size_t filesize(FILE *f);
 | 
			
		||||
@@ -81,7 +83,7 @@ int pid() SPDLOG_NOEXCEPT;
 | 
			
		||||
// Source: https://github.com/agauniyal/rang/
 | 
			
		||||
bool is_color_terminal() SPDLOG_NOEXCEPT;
 | 
			
		||||
 | 
			
		||||
// Detrmine if the terminal attached
 | 
			
		||||
// Determine if the terminal attached
 | 
			
		||||
// Source: https://github.com/agauniyal/rang/
 | 
			
		||||
bool in_terminal(FILE *file) SPDLOG_NOEXCEPT;
 | 
			
		||||
 | 
			
		||||
@@ -89,6 +91,17 @@ bool in_terminal(FILE *file) SPDLOG_NOEXCEPT;
 | 
			
		||||
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//"
 | 
			
		||||
filename_t dir_name(filename_t path);
 | 
			
		||||
 | 
			
		||||
// Create a dir from the given path.
 | 
			
		||||
// Return true if succeeded or if this dir already exists.
 | 
			
		||||
bool create_dir(filename_t path);
 | 
			
		||||
 | 
			
		||||
} // namespace os
 | 
			
		||||
} // namespace details
 | 
			
		||||
} // namespace spdlog
 | 
			
		||||
 
 | 
			
		||||
@@ -4,14 +4,14 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#ifndef SPDLOG_HEADER_ONLY
 | 
			
		||||
#include "spdlog/details/pattern_formatter.h"
 | 
			
		||||
#include <spdlog/details/pattern_formatter.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include "spdlog/details/fmt_helper.h"
 | 
			
		||||
#include "spdlog/details/log_msg.h"
 | 
			
		||||
#include "spdlog/details/os.h"
 | 
			
		||||
#include "spdlog/fmt/fmt.h"
 | 
			
		||||
#include "spdlog/formatter.h"
 | 
			
		||||
#include <spdlog/details/fmt_helper.h>
 | 
			
		||||
#include <spdlog/details/log_msg.h>
 | 
			
		||||
#include <spdlog/details/os.h>
 | 
			
		||||
#include <spdlog/fmt/fmt.h>
 | 
			
		||||
#include <spdlog/formatter.h>
 | 
			
		||||
 | 
			
		||||
#include <array>
 | 
			
		||||
#include <chrono>
 | 
			
		||||
@@ -39,47 +39,48 @@ public:
 | 
			
		||||
        : padinfo_(padinfo)
 | 
			
		||||
        , dest_(dest)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        if (padinfo_.width_ <= wrapped_size)
 | 
			
		||||
        remaining_pad_ = static_cast<long>(padinfo.width_) - static_cast<long>(wrapped_size);
 | 
			
		||||
        if (remaining_pad_ <= 0)
 | 
			
		||||
        {
 | 
			
		||||
            total_pad_ = 0;
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        total_pad_ = padinfo.width_ - wrapped_size;
 | 
			
		||||
        if (padinfo_.side_ == padding_info::left)
 | 
			
		||||
        {
 | 
			
		||||
            pad_it(total_pad_);
 | 
			
		||||
            total_pad_ = 0;
 | 
			
		||||
            pad_it(remaining_pad_);
 | 
			
		||||
            remaining_pad_ = 0;
 | 
			
		||||
        }
 | 
			
		||||
        else if (padinfo_.side_ == padding_info::center)
 | 
			
		||||
        {
 | 
			
		||||
            auto half_pad = total_pad_ / 2;
 | 
			
		||||
            auto reminder = total_pad_ & 1;
 | 
			
		||||
            auto half_pad = remaining_pad_ / 2;
 | 
			
		||||
            auto reminder = remaining_pad_ & 1;
 | 
			
		||||
            pad_it(half_pad);
 | 
			
		||||
            total_pad_ = half_pad + reminder; // for the right side
 | 
			
		||||
            remaining_pad_ = half_pad + reminder; // for the right side
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ~scoped_padder()
 | 
			
		||||
    {
 | 
			
		||||
        if (total_pad_)
 | 
			
		||||
        if (remaining_pad_ >= 0)
 | 
			
		||||
        {
 | 
			
		||||
            pad_it(total_pad_);
 | 
			
		||||
            pad_it(remaining_pad_);
 | 
			
		||||
        }
 | 
			
		||||
        else if (padinfo_.truncate_)
 | 
			
		||||
        {
 | 
			
		||||
            long new_size = static_cast<long>(dest_.size()) + remaining_pad_;
 | 
			
		||||
            dest_.resize(static_cast<size_t>(new_size));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    void pad_it(size_t count)
 | 
			
		||||
    void pad_it(long count)
 | 
			
		||||
    {
 | 
			
		||||
        // count = std::min(count, spaces_.size());
 | 
			
		||||
        assert(count <= spaces_.size());
 | 
			
		||||
        fmt_helper::append_string_view(string_view_t(spaces_.data(), count), dest_);
 | 
			
		||||
        fmt_helper::append_string_view(string_view_t(spaces_.data(), static_cast<size_t>(count)), dest_);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const padding_info &padinfo_;
 | 
			
		||||
    memory_buf_t &dest_;
 | 
			
		||||
    size_t total_pad_;
 | 
			
		||||
    long remaining_pad_;
 | 
			
		||||
    string_view_t spaces_{"                                                                ", 64};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -593,14 +594,7 @@ public:
 | 
			
		||||
        const size_t field_size = 6;
 | 
			
		||||
        ScopedPadder p(field_size, padinfo_, dest);
 | 
			
		||||
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
        int total_minutes = get_cached_offset(msg, tm_time);
 | 
			
		||||
#else
 | 
			
		||||
        // No need to chache under gcc,
 | 
			
		||||
        // it is very fast (already stored in tm.tm_gmtoff)
 | 
			
		||||
        (void)(msg);
 | 
			
		||||
        int total_minutes = os::utc_minutes_offset(tm_time);
 | 
			
		||||
#endif
 | 
			
		||||
        auto total_minutes = get_cached_offset(msg, tm_time);
 | 
			
		||||
        bool is_negative = total_minutes < 0;
 | 
			
		||||
        if (is_negative)
 | 
			
		||||
        {
 | 
			
		||||
@@ -619,7 +613,6 @@ public:
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    log_clock::time_point last_update_{std::chrono::seconds(0)};
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
    int offset_minutes_{0};
 | 
			
		||||
 | 
			
		||||
    int get_cached_offset(const log_msg &msg, const std::tm &tm_time)
 | 
			
		||||
@@ -632,7 +625,6 @@ private:
 | 
			
		||||
        }
 | 
			
		||||
        return offset_minutes_;
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Thread id
 | 
			
		||||
@@ -881,11 +873,13 @@ public:
 | 
			
		||||
        auto delta = (std::max)(msg.time - last_message_time_, log_clock::duration::zero());
 | 
			
		||||
        auto delta_units = std::chrono::duration_cast<DurationUnits>(delta);
 | 
			
		||||
        last_message_time_ = msg.time;
 | 
			
		||||
        ScopedPadder p(6, padinfo_, dest);
 | 
			
		||||
        fmt_helper::pad6(static_cast<size_t>(delta_units.count()), dest);
 | 
			
		||||
        auto delta_count = static_cast<size_t>(delta_units.count());
 | 
			
		||||
        auto n_digits = static_cast<size_t>(fmt_helper::count_digits(delta_count));
 | 
			
		||||
        ScopedPadder p(n_digits, padinfo_, dest);
 | 
			
		||||
        fmt_helper::append_int(delta_count, dest);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
private:
 | 
			
		||||
    log_clock::time_point last_message_time_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -904,8 +898,6 @@ public:
 | 
			
		||||
        using std::chrono::milliseconds;
 | 
			
		||||
        using std::chrono::seconds;
 | 
			
		||||
 | 
			
		||||
#ifndef SPDLOG_NO_DATETIME
 | 
			
		||||
 | 
			
		||||
        // cache the date/time part for the next second.
 | 
			
		||||
        auto duration = msg.time.time_since_epoch();
 | 
			
		||||
        auto secs = duration_cast<seconds>(duration);
 | 
			
		||||
@@ -941,10 +933,6 @@ public:
 | 
			
		||||
        dest.push_back(']');
 | 
			
		||||
        dest.push_back(' ');
 | 
			
		||||
 | 
			
		||||
#else // no datetime needed
 | 
			
		||||
        (void)tm_time;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifndef SPDLOG_NO_NAME
 | 
			
		||||
        if (msg.logger_name.size() > 0)
 | 
			
		||||
        {
 | 
			
		||||
@@ -1014,14 +1002,13 @@ SPDLOG_INLINE std::unique_ptr<formatter> pattern_formatter::clone() const
 | 
			
		||||
 | 
			
		||||
SPDLOG_INLINE void pattern_formatter::format(const details::log_msg &msg, memory_buf_t &dest)
 | 
			
		||||
{
 | 
			
		||||
#ifndef SPDLOG_NO_DATETIME
 | 
			
		||||
    auto secs = std::chrono::duration_cast<std::chrono::seconds>(msg.time.time_since_epoch());
 | 
			
		||||
    if (secs != last_log_secs_)
 | 
			
		||||
    {
 | 
			
		||||
        cached_tm_ = get_time_(msg);
 | 
			
		||||
        last_log_secs_ = secs;
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    for (auto &f : formatters_)
 | 
			
		||||
    {
 | 
			
		||||
        f->format(msg, cached_tm_, dest);
 | 
			
		||||
@@ -1225,7 +1212,7 @@ SPDLOG_INLINE void pattern_formatter::handle_flag_(char flag, details::padding_i
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Extract given pad spec (e.g. %8X)
 | 
			
		||||
// Extract given pad spec (e.g. %8X, %=8X, %-8!X, %8!X, %=8!X, %-8!X, %+8!X)
 | 
			
		||||
// Advance the given it pass the end of the padding spec found (if any)
 | 
			
		||||
// Return padding.
 | 
			
		||||
SPDLOG_INLINE details::padding_info pattern_formatter::handle_padspec_(std::string::const_iterator &it, std::string::const_iterator end)
 | 
			
		||||
@@ -1256,7 +1243,7 @@ SPDLOG_INLINE details::padding_info pattern_formatter::handle_padspec_(std::stri
 | 
			
		||||
 | 
			
		||||
    if (it == end || !std::isdigit(static_cast<unsigned char>(*it)))
 | 
			
		||||
    {
 | 
			
		||||
        return padding_info{0, side};
 | 
			
		||||
        return padding_info{}; // no padding if no digit found here
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    auto width = static_cast<size_t>(*it) - '0';
 | 
			
		||||
@@ -1265,7 +1252,20 @@ SPDLOG_INLINE details::padding_info pattern_formatter::handle_padspec_(std::stri
 | 
			
		||||
        auto digit = static_cast<size_t>(*it) - '0';
 | 
			
		||||
        width = width * 10 + digit;
 | 
			
		||||
    }
 | 
			
		||||
    return details::padding_info{std::min<size_t>(width, max_width), side};
 | 
			
		||||
 | 
			
		||||
    // search for the optional truncate marker '!'
 | 
			
		||||
    bool truncate;
 | 
			
		||||
    if (it != end && *it == '!')
 | 
			
		||||
    {
 | 
			
		||||
        truncate = true;
 | 
			
		||||
        ++it;
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        truncate = false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return details::padding_info{std::min<size_t>(width, max_width), side, truncate};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
SPDLOG_INLINE void pattern_formatter::compile_pattern_(const std::string &pattern)
 | 
			
		||||
 
 | 
			
		||||
@@ -3,10 +3,10 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "spdlog/common.h"
 | 
			
		||||
#include "spdlog/details/log_msg.h"
 | 
			
		||||
#include "spdlog/details/os.h"
 | 
			
		||||
#include "spdlog/formatter.h"
 | 
			
		||||
#include <spdlog/common.h>
 | 
			
		||||
#include <spdlog/details/log_msg.h>
 | 
			
		||||
#include <spdlog/details/os.h>
 | 
			
		||||
#include <spdlog/formatter.h>
 | 
			
		||||
 | 
			
		||||
#include <chrono>
 | 
			
		||||
#include <ctime>
 | 
			
		||||
@@ -29,17 +29,21 @@ struct padding_info
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    padding_info() = default;
 | 
			
		||||
    padding_info(size_t width, padding_info::pad_side side)
 | 
			
		||||
    padding_info(size_t width, padding_info::pad_side side, bool truncate)
 | 
			
		||||
        : width_(width)
 | 
			
		||||
        , side_(side)
 | 
			
		||||
        , truncate_(truncate)
 | 
			
		||||
        , enabled_(true)
 | 
			
		||||
    {}
 | 
			
		||||
 | 
			
		||||
    bool enabled() const
 | 
			
		||||
    {
 | 
			
		||||
        return width_ != 0;
 | 
			
		||||
        return enabled_;
 | 
			
		||||
    }
 | 
			
		||||
    const size_t width_ = 0;
 | 
			
		||||
    const pad_side side_ = left;
 | 
			
		||||
    bool truncate_ = false;
 | 
			
		||||
    bool enabled_ = false;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class flag_formatter
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#ifndef SPDLOG_HEADER_ONLY
 | 
			
		||||
#include "spdlog/details/periodic_worker.h"
 | 
			
		||||
#include <spdlog/details/periodic_worker.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
namespace spdlog {
 | 
			
		||||
 
 | 
			
		||||
@@ -4,20 +4,20 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#ifndef SPDLOG_HEADER_ONLY
 | 
			
		||||
#include "spdlog/details/registry.h"
 | 
			
		||||
#include <spdlog/details/registry.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include "spdlog/common.h"
 | 
			
		||||
#include "spdlog/details/periodic_worker.h"
 | 
			
		||||
#include "spdlog/logger.h"
 | 
			
		||||
#include "spdlog/details/pattern_formatter.h"
 | 
			
		||||
#include <spdlog/common.h>
 | 
			
		||||
#include <spdlog/details/periodic_worker.h>
 | 
			
		||||
#include <spdlog/logger.h>
 | 
			
		||||
#include <spdlog/details/pattern_formatter.h>
 | 
			
		||||
 | 
			
		||||
#ifndef SPDLOG_DISABLE_DEFAULT_LOGGER
 | 
			
		||||
// support for the default stdout color logger
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
#include "spdlog/sinks/wincolor_sink.h"
 | 
			
		||||
#include <spdlog/sinks/wincolor_sink.h>
 | 
			
		||||
#else
 | 
			
		||||
#include "spdlog/sinks/ansicolor_sink.h"
 | 
			
		||||
#include <spdlog/sinks/ansicolor_sink.h>
 | 
			
		||||
#endif
 | 
			
		||||
#endif // SPDLOG_DISABLE_DEFAULT_LOGGER
 | 
			
		||||
 | 
			
		||||
@@ -254,10 +254,10 @@ SPDLOG_INLINE std::recursive_mutex ®istry::tp_mutex()
 | 
			
		||||
    return tp_mutex_;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
SPDLOG_INLINE void registry::set_automatic_registration(bool automatic_regsistration)
 | 
			
		||||
SPDLOG_INLINE void registry::set_automatic_registration(bool automatic_registration)
 | 
			
		||||
{
 | 
			
		||||
    std::lock_guard<std::mutex> lock(logger_map_mutex_);
 | 
			
		||||
    automatic_registration_ = automatic_regsistration;
 | 
			
		||||
    automatic_registration_ = automatic_registration;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
SPDLOG_INLINE registry ®istry::instance()
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,7 @@
 | 
			
		||||
// If user requests a non existing logger, nullptr will be returned
 | 
			
		||||
// This class is thread safe
 | 
			
		||||
 | 
			
		||||
#include "spdlog/common.h"
 | 
			
		||||
#include <spdlog/common.h>
 | 
			
		||||
 | 
			
		||||
#include <chrono>
 | 
			
		||||
#include <functional>
 | 
			
		||||
@@ -77,7 +77,7 @@ public:
 | 
			
		||||
 | 
			
		||||
    std::recursive_mutex &tp_mutex();
 | 
			
		||||
 | 
			
		||||
    void set_automatic_registration(bool automatic_regsistration);
 | 
			
		||||
    void set_automatic_registration(bool automatic_registration);
 | 
			
		||||
 | 
			
		||||
    static registry &instance();
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -4,10 +4,11 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#ifndef SPDLOG_HEADER_ONLY
 | 
			
		||||
#include "spdlog/details/thread_pool.h"
 | 
			
		||||
#include <spdlog/details/thread_pool.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include "spdlog/common.h"
 | 
			
		||||
#include <spdlog/common.h>
 | 
			
		||||
#include <cassert>
 | 
			
		||||
 | 
			
		||||
namespace spdlog {
 | 
			
		||||
namespace details {
 | 
			
		||||
@@ -81,7 +82,7 @@ void SPDLOG_INLINE thread_pool::post_async_msg_(async_msg &&new_msg, async_overf
 | 
			
		||||
 | 
			
		||||
void SPDLOG_INLINE thread_pool::worker_loop_()
 | 
			
		||||
{
 | 
			
		||||
    while (process_next_msg_()) {};
 | 
			
		||||
    while (process_next_msg_()) {}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// process next message in the queue
 | 
			
		||||
@@ -98,24 +99,20 @@ bool SPDLOG_INLINE thread_pool::process_next_msg_()
 | 
			
		||||
 | 
			
		||||
    switch (incoming_async_msg.msg_type)
 | 
			
		||||
    {
 | 
			
		||||
    case async_msg_type::log:
 | 
			
		||||
    {
 | 
			
		||||
    case async_msg_type::log: {
 | 
			
		||||
        incoming_async_msg.worker_ptr->backend_sink_it_(incoming_async_msg);
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    case async_msg_type::flush:
 | 
			
		||||
    {
 | 
			
		||||
    case async_msg_type::flush: {
 | 
			
		||||
        incoming_async_msg.worker_ptr->backend_flush_();
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    case async_msg_type::terminate:
 | 
			
		||||
    {
 | 
			
		||||
    case async_msg_type::terminate: {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    default:
 | 
			
		||||
    {
 | 
			
		||||
    default: {
 | 
			
		||||
        assert(false && "Unexpected async_msg_type");
 | 
			
		||||
    }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -3,9 +3,9 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "spdlog/details/log_msg_buffer.h"
 | 
			
		||||
#include "spdlog/details/mpmc_blocking_q.h"
 | 
			
		||||
#include "spdlog/details/os.h"
 | 
			
		||||
#include <spdlog/details/log_msg_buffer.h>
 | 
			
		||||
#include <spdlog/details/mpmc_blocking_q.h>
 | 
			
		||||
#include <spdlog/details/os.h>
 | 
			
		||||
 | 
			
		||||
#include <chrono>
 | 
			
		||||
#include <memory>
 | 
			
		||||
@@ -27,7 +27,7 @@ enum class async_msg_type
 | 
			
		||||
    terminate
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#include "spdlog/details/log_msg_buffer.h"
 | 
			
		||||
#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
 | 
			
		||||
 
 | 
			
		||||
@@ -16,16 +16,291 @@
 | 
			
		||||
#include <locale>
 | 
			
		||||
#include <sstream>
 | 
			
		||||
 | 
			
		||||
// enable safe chrono durations, unless explicitly disabled
 | 
			
		||||
FMT_BEGIN_NAMESPACE
 | 
			
		||||
 | 
			
		||||
// Enable safe chrono durations, unless explicitly disabled.
 | 
			
		||||
#ifndef FMT_SAFE_DURATION_CAST
 | 
			
		||||
#  define FMT_SAFE_DURATION_CAST 1
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if FMT_SAFE_DURATION_CAST
 | 
			
		||||
#  include "safe-duration-cast.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
FMT_BEGIN_NAMESPACE
 | 
			
		||||
// For conversion between std::chrono::durations without undefined
 | 
			
		||||
// behaviour or erroneous results.
 | 
			
		||||
// This is a stripped down version of duration_cast, for inclusion in fmt.
 | 
			
		||||
// See https://github.com/pauldreik/safe_duration_cast
 | 
			
		||||
//
 | 
			
		||||
// Copyright Paul Dreik 2019
 | 
			
		||||
namespace safe_duration_cast {
 | 
			
		||||
 | 
			
		||||
template <typename To, typename From,
 | 
			
		||||
          FMT_ENABLE_IF(!std::is_same<From, To>::value &&
 | 
			
		||||
                        std::numeric_limits<From>::is_signed ==
 | 
			
		||||
                            std::numeric_limits<To>::is_signed)>
 | 
			
		||||
FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
 | 
			
		||||
  ec = 0;
 | 
			
		||||
  using F = std::numeric_limits<From>;
 | 
			
		||||
  using T = std::numeric_limits<To>;
 | 
			
		||||
  static_assert(F::is_integer, "From must be integral");
 | 
			
		||||
  static_assert(T::is_integer, "To must be integral");
 | 
			
		||||
 | 
			
		||||
  // A and B are both signed, or both unsigned.
 | 
			
		||||
  if (F::digits <= T::digits) {
 | 
			
		||||
    // From fits in To without any problem.
 | 
			
		||||
  } else {
 | 
			
		||||
    // From does not always fit in To, resort to a dynamic check.
 | 
			
		||||
    if (from < T::min() || from > T::max()) {
 | 
			
		||||
      // outside range.
 | 
			
		||||
      ec = 1;
 | 
			
		||||
      return {};
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return static_cast<To>(from);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * converts From to To, without loss. If the dynamic value of from
 | 
			
		||||
 * can't be converted to To without loss, ec is set.
 | 
			
		||||
 */
 | 
			
		||||
template <typename To, typename From,
 | 
			
		||||
          FMT_ENABLE_IF(!std::is_same<From, To>::value &&
 | 
			
		||||
                        std::numeric_limits<From>::is_signed !=
 | 
			
		||||
                            std::numeric_limits<To>::is_signed)>
 | 
			
		||||
FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
 | 
			
		||||
  ec = 0;
 | 
			
		||||
  using F = std::numeric_limits<From>;
 | 
			
		||||
  using T = std::numeric_limits<To>;
 | 
			
		||||
  static_assert(F::is_integer, "From must be integral");
 | 
			
		||||
  static_assert(T::is_integer, "To must be integral");
 | 
			
		||||
 | 
			
		||||
  if (F::is_signed && !T::is_signed) {
 | 
			
		||||
    // From may be negative, not allowed!
 | 
			
		||||
    if (fmt::internal::is_negative(from)) {
 | 
			
		||||
      ec = 1;
 | 
			
		||||
      return {};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // From is positive. Can it always fit in To?
 | 
			
		||||
    if (F::digits <= T::digits) {
 | 
			
		||||
      // yes, From always fits in To.
 | 
			
		||||
    } else {
 | 
			
		||||
      // from may not fit in To, we have to do a dynamic check
 | 
			
		||||
      if (from > static_cast<From>(T::max())) {
 | 
			
		||||
        ec = 1;
 | 
			
		||||
        return {};
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!F::is_signed && T::is_signed) {
 | 
			
		||||
    // can from be held in To?
 | 
			
		||||
    if (F::digits < T::digits) {
 | 
			
		||||
      // yes, From always fits in To.
 | 
			
		||||
    } else {
 | 
			
		||||
      // from may not fit in To, we have to do a dynamic check
 | 
			
		||||
      if (from > static_cast<From>(T::max())) {
 | 
			
		||||
        // outside range.
 | 
			
		||||
        ec = 1;
 | 
			
		||||
        return {};
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // reaching here means all is ok for lossless conversion.
 | 
			
		||||
  return static_cast<To>(from);
 | 
			
		||||
 | 
			
		||||
}  // function
 | 
			
		||||
 | 
			
		||||
template <typename To, typename From,
 | 
			
		||||
          FMT_ENABLE_IF(std::is_same<From, To>::value)>
 | 
			
		||||
FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
 | 
			
		||||
  ec = 0;
 | 
			
		||||
  return from;
 | 
			
		||||
}  // function
 | 
			
		||||
 | 
			
		||||
// clang-format off
 | 
			
		||||
/**
 | 
			
		||||
 * converts From to To if possible, otherwise ec is set.
 | 
			
		||||
 *
 | 
			
		||||
 * input                            |    output
 | 
			
		||||
 * ---------------------------------|---------------
 | 
			
		||||
 * NaN                              | NaN
 | 
			
		||||
 * Inf                              | Inf
 | 
			
		||||
 * normal, fits in output           | converted (possibly lossy)
 | 
			
		||||
 * normal, does not fit in output   | ec is set
 | 
			
		||||
 * subnormal                        | best effort
 | 
			
		||||
 * -Inf                             | -Inf
 | 
			
		||||
 */
 | 
			
		||||
// clang-format on
 | 
			
		||||
template <typename To, typename From,
 | 
			
		||||
          FMT_ENABLE_IF(!std::is_same<From, To>::value)>
 | 
			
		||||
FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
 | 
			
		||||
  ec = 0;
 | 
			
		||||
  using T = std::numeric_limits<To>;
 | 
			
		||||
  static_assert(std::is_floating_point<From>::value, "From must be floating");
 | 
			
		||||
  static_assert(std::is_floating_point<To>::value, "To must be floating");
 | 
			
		||||
 | 
			
		||||
  // catch the only happy case
 | 
			
		||||
  if (std::isfinite(from)) {
 | 
			
		||||
    if (from >= T::lowest() && from <= T::max()) {
 | 
			
		||||
      return static_cast<To>(from);
 | 
			
		||||
    }
 | 
			
		||||
    // not within range.
 | 
			
		||||
    ec = 1;
 | 
			
		||||
    return {};
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // nan and inf will be preserved
 | 
			
		||||
  return static_cast<To>(from);
 | 
			
		||||
}  // function
 | 
			
		||||
 | 
			
		||||
template <typename To, typename From,
 | 
			
		||||
          FMT_ENABLE_IF(std::is_same<From, To>::value)>
 | 
			
		||||
FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
 | 
			
		||||
  ec = 0;
 | 
			
		||||
  static_assert(std::is_floating_point<From>::value, "From must be floating");
 | 
			
		||||
  return from;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * safe duration cast between integral durations
 | 
			
		||||
 */
 | 
			
		||||
template <typename To, typename FromRep, typename FromPeriod,
 | 
			
		||||
          FMT_ENABLE_IF(std::is_integral<FromRep>::value),
 | 
			
		||||
          FMT_ENABLE_IF(std::is_integral<typename To::rep>::value)>
 | 
			
		||||
To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
 | 
			
		||||
                      int& ec) {
 | 
			
		||||
  using From = std::chrono::duration<FromRep, FromPeriod>;
 | 
			
		||||
  ec = 0;
 | 
			
		||||
  // the basic idea is that we need to convert from count() in the from type
 | 
			
		||||
  // to count() in the To type, by multiplying it with this:
 | 
			
		||||
  struct Factor
 | 
			
		||||
      : std::ratio_divide<typename From::period, typename To::period> {};
 | 
			
		||||
 | 
			
		||||
  static_assert(Factor::num > 0, "num must be positive");
 | 
			
		||||
  static_assert(Factor::den > 0, "den must be positive");
 | 
			
		||||
 | 
			
		||||
  // the conversion is like this: multiply from.count() with Factor::num
 | 
			
		||||
  // /Factor::den and convert it to To::rep, all this without
 | 
			
		||||
  // overflow/underflow. let's start by finding a suitable type that can hold
 | 
			
		||||
  // both To, From and Factor::num
 | 
			
		||||
  using IntermediateRep =
 | 
			
		||||
      typename std::common_type<typename From::rep, typename To::rep,
 | 
			
		||||
                                decltype(Factor::num)>::type;
 | 
			
		||||
 | 
			
		||||
  // safe conversion to IntermediateRep
 | 
			
		||||
  IntermediateRep count =
 | 
			
		||||
      lossless_integral_conversion<IntermediateRep>(from.count(), ec);
 | 
			
		||||
  if (ec) {
 | 
			
		||||
    return {};
 | 
			
		||||
  }
 | 
			
		||||
  // multiply with Factor::num without overflow or underflow
 | 
			
		||||
  if (Factor::num != 1) {
 | 
			
		||||
    const auto max1 = internal::max_value<IntermediateRep>() / Factor::num;
 | 
			
		||||
    if (count > max1) {
 | 
			
		||||
      ec = 1;
 | 
			
		||||
      return {};
 | 
			
		||||
    }
 | 
			
		||||
    const auto min1 = std::numeric_limits<IntermediateRep>::min() / Factor::num;
 | 
			
		||||
    if (count < min1) {
 | 
			
		||||
      ec = 1;
 | 
			
		||||
      return {};
 | 
			
		||||
    }
 | 
			
		||||
    count *= Factor::num;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // this can't go wrong, right? den>0 is checked earlier.
 | 
			
		||||
  if (Factor::den != 1) {
 | 
			
		||||
    count /= Factor::den;
 | 
			
		||||
  }
 | 
			
		||||
  // convert to the to type, safely
 | 
			
		||||
  using ToRep = typename To::rep;
 | 
			
		||||
  const ToRep tocount = lossless_integral_conversion<ToRep>(count, ec);
 | 
			
		||||
  if (ec) {
 | 
			
		||||
    return {};
 | 
			
		||||
  }
 | 
			
		||||
  return To{tocount};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * safe duration_cast between floating point durations
 | 
			
		||||
 */
 | 
			
		||||
template <typename To, typename FromRep, typename FromPeriod,
 | 
			
		||||
          FMT_ENABLE_IF(std::is_floating_point<FromRep>::value),
 | 
			
		||||
          FMT_ENABLE_IF(std::is_floating_point<typename To::rep>::value)>
 | 
			
		||||
To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
 | 
			
		||||
                      int& ec) {
 | 
			
		||||
  using From = std::chrono::duration<FromRep, FromPeriod>;
 | 
			
		||||
  ec = 0;
 | 
			
		||||
  if (std::isnan(from.count())) {
 | 
			
		||||
    // nan in, gives nan out. easy.
 | 
			
		||||
    return To{std::numeric_limits<typename To::rep>::quiet_NaN()};
 | 
			
		||||
  }
 | 
			
		||||
  // maybe we should also check if from is denormal, and decide what to do about
 | 
			
		||||
  // it.
 | 
			
		||||
 | 
			
		||||
  // +-inf should be preserved.
 | 
			
		||||
  if (std::isinf(from.count())) {
 | 
			
		||||
    return To{from.count()};
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // the basic idea is that we need to convert from count() in the from type
 | 
			
		||||
  // to count() in the To type, by multiplying it with this:
 | 
			
		||||
  struct Factor
 | 
			
		||||
      : std::ratio_divide<typename From::period, typename To::period> {};
 | 
			
		||||
 | 
			
		||||
  static_assert(Factor::num > 0, "num must be positive");
 | 
			
		||||
  static_assert(Factor::den > 0, "den must be positive");
 | 
			
		||||
 | 
			
		||||
  // the conversion is like this: multiply from.count() with Factor::num
 | 
			
		||||
  // /Factor::den and convert it to To::rep, all this without
 | 
			
		||||
  // overflow/underflow. let's start by finding a suitable type that can hold
 | 
			
		||||
  // both To, From and Factor::num
 | 
			
		||||
  using IntermediateRep =
 | 
			
		||||
      typename std::common_type<typename From::rep, typename To::rep,
 | 
			
		||||
                                decltype(Factor::num)>::type;
 | 
			
		||||
 | 
			
		||||
  // force conversion of From::rep -> IntermediateRep to be safe,
 | 
			
		||||
  // even if it will never happen be narrowing in this context.
 | 
			
		||||
  IntermediateRep count =
 | 
			
		||||
      safe_float_conversion<IntermediateRep>(from.count(), ec);
 | 
			
		||||
  if (ec) {
 | 
			
		||||
    return {};
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // multiply with Factor::num without overflow or underflow
 | 
			
		||||
  if (Factor::num != 1) {
 | 
			
		||||
    constexpr auto max1 = internal::max_value<IntermediateRep>() /
 | 
			
		||||
                          static_cast<IntermediateRep>(Factor::num);
 | 
			
		||||
    if (count > max1) {
 | 
			
		||||
      ec = 1;
 | 
			
		||||
      return {};
 | 
			
		||||
    }
 | 
			
		||||
    constexpr auto min1 = std::numeric_limits<IntermediateRep>::lowest() /
 | 
			
		||||
                          static_cast<IntermediateRep>(Factor::num);
 | 
			
		||||
    if (count < min1) {
 | 
			
		||||
      ec = 1;
 | 
			
		||||
      return {};
 | 
			
		||||
    }
 | 
			
		||||
    count *= static_cast<IntermediateRep>(Factor::num);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // this can't go wrong, right? den>0 is checked earlier.
 | 
			
		||||
  if (Factor::den != 1) {
 | 
			
		||||
    using common_t = typename std::common_type<IntermediateRep, intmax_t>::type;
 | 
			
		||||
    count /= static_cast<common_t>(Factor::den);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // convert to the to type, safely
 | 
			
		||||
  using ToRep = typename To::rep;
 | 
			
		||||
 | 
			
		||||
  const ToRep tocount = safe_float_conversion<ToRep>(count, ec);
 | 
			
		||||
  if (ec) {
 | 
			
		||||
    return {};
 | 
			
		||||
  }
 | 
			
		||||
  return To{tocount};
 | 
			
		||||
}
 | 
			
		||||
}  // namespace safe_duration_cast
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
// Prevents expansion of a preceding token as a function-style macro.
 | 
			
		||||
// Usage: f FMT_NOMACRO()
 | 
			
		||||
@@ -403,7 +678,7 @@ inline bool isfinite(T value) {
 | 
			
		||||
  return std::isfinite(value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Convers value to int and checks that it's in the range [0, upper).
 | 
			
		||||
// Converts value to int and checks that it's in the range [0, upper).
 | 
			
		||||
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
 | 
			
		||||
inline int to_nonnegative_int(T value, int upper) {
 | 
			
		||||
  FMT_ASSERT(value >= 0 && value <= upper, "invalid value");
 | 
			
		||||
@@ -582,8 +857,8 @@ struct chrono_formatter {
 | 
			
		||||
  void write(Rep value, int width) {
 | 
			
		||||
    write_sign();
 | 
			
		||||
    if (isnan(value)) return write_nan();
 | 
			
		||||
    uint32_or_64_t<int> n = to_unsigned(
 | 
			
		||||
        to_nonnegative_int(value, (std::numeric_limits<int>::max)()));
 | 
			
		||||
    uint32_or_64_or_128_t<int> n =
 | 
			
		||||
        to_unsigned(to_nonnegative_int(value, max_value<int>()));
 | 
			
		||||
    int num_digits = internal::count_digits(n);
 | 
			
		||||
    if (width > num_digits) out = std::fill_n(out, width - num_digits, '0');
 | 
			
		||||
    out = format_decimal<char_type>(out, n, num_digits);
 | 
			
		||||
@@ -728,7 +1003,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
 | 
			
		||||
 | 
			
		||||
  struct spec_handler {
 | 
			
		||||
    formatter& f;
 | 
			
		||||
    basic_parse_context<Char>& context;
 | 
			
		||||
    basic_format_parse_context<Char>& context;
 | 
			
		||||
    basic_string_view<Char> format_str;
 | 
			
		||||
 | 
			
		||||
    template <typename Id> FMT_CONSTEXPR arg_ref_type make_arg_ref(Id arg_id) {
 | 
			
		||||
@@ -738,8 +1013,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
 | 
			
		||||
 | 
			
		||||
    FMT_CONSTEXPR arg_ref_type make_arg_ref(basic_string_view<Char> arg_id) {
 | 
			
		||||
      context.check_arg_id(arg_id);
 | 
			
		||||
      const auto str_val = internal::string_view_metadata(format_str, arg_id);
 | 
			
		||||
      return arg_ref_type(str_val);
 | 
			
		||||
      return arg_ref_type(arg_id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    FMT_CONSTEXPR arg_ref_type make_arg_ref(internal::auto_id) {
 | 
			
		||||
@@ -750,7 +1024,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
 | 
			
		||||
    void on_fill(Char fill) { f.specs.fill[0] = fill; }
 | 
			
		||||
    void on_align(align_t align) { f.specs.align = align; }
 | 
			
		||||
    void on_width(unsigned width) { f.specs.width = width; }
 | 
			
		||||
    void on_precision(unsigned precision) { f.precision = precision; }
 | 
			
		||||
    void on_precision(unsigned _precision) { f.precision = _precision; }
 | 
			
		||||
    void end_precision() {}
 | 
			
		||||
 | 
			
		||||
    template <typename Id> void on_dynamic_width(Id arg_id) {
 | 
			
		||||
@@ -762,13 +1036,13 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  using iterator = typename basic_parse_context<Char>::iterator;
 | 
			
		||||
  using iterator = typename basic_format_parse_context<Char>::iterator;
 | 
			
		||||
  struct parse_range {
 | 
			
		||||
    iterator begin;
 | 
			
		||||
    iterator end;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  FMT_CONSTEXPR parse_range do_parse(basic_parse_context<Char>& ctx) {
 | 
			
		||||
  FMT_CONSTEXPR parse_range do_parse(basic_format_parse_context<Char>& ctx) {
 | 
			
		||||
    auto begin = ctx.begin(), end = ctx.end();
 | 
			
		||||
    if (begin == end || *begin == '}') return {begin, begin};
 | 
			
		||||
    spec_handler handler{*this, ctx, format_str};
 | 
			
		||||
@@ -789,7 +1063,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
 | 
			
		||||
 public:
 | 
			
		||||
  formatter() : precision(-1) {}
 | 
			
		||||
 | 
			
		||||
  FMT_CONSTEXPR auto parse(basic_parse_context<Char>& ctx)
 | 
			
		||||
  FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
 | 
			
		||||
      -> decltype(ctx.begin()) {
 | 
			
		||||
    auto range = do_parse(ctx);
 | 
			
		||||
    format_str = basic_string_view<Char>(
 | 
			
		||||
@@ -806,10 +1080,10 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
 | 
			
		||||
    auto out = std::back_inserter(buf);
 | 
			
		||||
    using range = internal::output_range<decltype(ctx.out()), Char>;
 | 
			
		||||
    internal::basic_writer<range> w(range(ctx.out()));
 | 
			
		||||
    internal::handle_dynamic_spec<internal::width_checker>(
 | 
			
		||||
        specs.width, width_ref, ctx, format_str.begin());
 | 
			
		||||
    internal::handle_dynamic_spec<internal::width_checker>(specs.width,
 | 
			
		||||
                                                           width_ref, ctx);
 | 
			
		||||
    internal::handle_dynamic_spec<internal::precision_checker>(
 | 
			
		||||
        precision, precision_ref, ctx, format_str.begin());
 | 
			
		||||
        precision, precision_ref, ctx);
 | 
			
		||||
    if (begin == end || *begin == '}') {
 | 
			
		||||
      out = internal::format_chrono_duration_value(out, d.count(), precision);
 | 
			
		||||
      internal::format_chrono_duration_unit<Period>(out);
 | 
			
		||||
 
 | 
			
		||||
@@ -299,15 +299,15 @@ class text_style {
 | 
			
		||||
    return static_cast<uint8_t>(ems) != 0;
 | 
			
		||||
  }
 | 
			
		||||
  FMT_CONSTEXPR internal::color_type get_foreground() const FMT_NOEXCEPT {
 | 
			
		||||
    assert(has_foreground() && "no foreground specified for this style");
 | 
			
		||||
    FMT_ASSERT(has_foreground(), "no foreground specified for this style");
 | 
			
		||||
    return foreground_color;
 | 
			
		||||
  }
 | 
			
		||||
  FMT_CONSTEXPR internal::color_type get_background() const FMT_NOEXCEPT {
 | 
			
		||||
    assert(has_background() && "no background specified for this style");
 | 
			
		||||
    FMT_ASSERT(has_background(), "no background specified for this style");
 | 
			
		||||
    return background_color;
 | 
			
		||||
  }
 | 
			
		||||
  FMT_CONSTEXPR emphasis get_emphasis() const FMT_NOEXCEPT {
 | 
			
		||||
    assert(has_emphasis() && "no emphasis specified for this style");
 | 
			
		||||
    FMT_ASSERT(has_emphasis(), "no emphasis specified for this style");
 | 
			
		||||
    return ems;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -470,58 +470,41 @@ inline void reset_color(basic_memory_buffer<Char>& buffer) FMT_NOEXCEPT {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename Char>
 | 
			
		||||
std::basic_string<Char> vformat(const text_style& ts,
 | 
			
		||||
                                basic_string_view<Char> format_str,
 | 
			
		||||
                                basic_format_args<buffer_context<Char> > args) {
 | 
			
		||||
  basic_memory_buffer<Char> buffer;
 | 
			
		||||
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;
 | 
			
		||||
    ansi_color_escape<Char> escape = make_emphasis<Char>(ts.get_emphasis());
 | 
			
		||||
    buffer.append(escape.begin(), escape.end());
 | 
			
		||||
    auto emphasis = internal::make_emphasis<Char>(ts.get_emphasis());
 | 
			
		||||
    buf.append(emphasis.begin(), emphasis.end());
 | 
			
		||||
  }
 | 
			
		||||
  if (ts.has_foreground()) {
 | 
			
		||||
    has_style = true;
 | 
			
		||||
    ansi_color_escape<Char> escape =
 | 
			
		||||
        make_foreground_color<Char>(ts.get_foreground());
 | 
			
		||||
    buffer.append(escape.begin(), escape.end());
 | 
			
		||||
    auto foreground =
 | 
			
		||||
        internal::make_foreground_color<Char>(ts.get_foreground());
 | 
			
		||||
    buf.append(foreground.begin(), foreground.end());
 | 
			
		||||
  }
 | 
			
		||||
  if (ts.has_background()) {
 | 
			
		||||
    has_style = true;
 | 
			
		||||
    ansi_color_escape<Char> escape =
 | 
			
		||||
        make_background_color<Char>(ts.get_background());
 | 
			
		||||
    buffer.append(escape.begin(), escape.end());
 | 
			
		||||
    auto background =
 | 
			
		||||
        internal::make_background_color<Char>(ts.get_background());
 | 
			
		||||
    buf.append(background.begin(), background.end());
 | 
			
		||||
  }
 | 
			
		||||
  internal::vformat_to(buffer, format_str, args);
 | 
			
		||||
  vformat_to(buf, format_str, args);
 | 
			
		||||
  if (has_style) {
 | 
			
		||||
    reset_color<Char>(buffer);
 | 
			
		||||
    internal::reset_color<Char>(buf);
 | 
			
		||||
  }
 | 
			
		||||
  return fmt::to_string(buffer);
 | 
			
		||||
}
 | 
			
		||||
}  // namespace internal
 | 
			
		||||
 | 
			
		||||
template <typename S, typename Char = char_t<S> >
 | 
			
		||||
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) {
 | 
			
		||||
  bool has_style = false;
 | 
			
		||||
  if (ts.has_emphasis()) {
 | 
			
		||||
    has_style = true;
 | 
			
		||||
    internal::fputs<Char>(internal::make_emphasis<Char>(ts.get_emphasis()), f);
 | 
			
		||||
  }
 | 
			
		||||
  if (ts.has_foreground()) {
 | 
			
		||||
    has_style = true;
 | 
			
		||||
    internal::fputs<Char>(
 | 
			
		||||
        internal::make_foreground_color<Char>(ts.get_foreground()), f);
 | 
			
		||||
  }
 | 
			
		||||
  if (ts.has_background()) {
 | 
			
		||||
    has_style = true;
 | 
			
		||||
    internal::fputs<Char>(
 | 
			
		||||
        internal::make_background_color<Char>(ts.get_background()), f);
 | 
			
		||||
  }
 | 
			
		||||
  vprint(f, format, args);
 | 
			
		||||
  if (has_style) {
 | 
			
		||||
    internal::reset_color<Char>(f);
 | 
			
		||||
  }
 | 
			
		||||
            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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -536,7 +519,7 @@ template <typename S, typename... Args,
 | 
			
		||||
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> >;
 | 
			
		||||
  using context = buffer_context<char_t<S>>;
 | 
			
		||||
  format_arg_store<context, Args...> as{args...};
 | 
			
		||||
  vprint(f, ts, format_str, basic_format_args<context>(as));
 | 
			
		||||
}
 | 
			
		||||
@@ -554,11 +537,13 @@ 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> >
 | 
			
		||||
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<Char> > args) {
 | 
			
		||||
  return internal::vformat(ts, to_string_view(format_str), args);
 | 
			
		||||
    basic_format_args<buffer_context<Char>> args) {
 | 
			
		||||
  basic_memory_buffer<Char> buf;
 | 
			
		||||
  internal::vformat_to(buf, ts, to_string_view(format_str), args);
 | 
			
		||||
  return fmt::to_string(buf);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -573,11 +558,11 @@ inline std::basic_string<Char> vformat(
 | 
			
		||||
                                      "The answer is {}", 42);
 | 
			
		||||
  \endrst
 | 
			
		||||
*/
 | 
			
		||||
template <typename S, typename... Args, typename Char = char_t<S> >
 | 
			
		||||
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 internal::vformat(ts, to_string_view(format_str),
 | 
			
		||||
                           {internal::make_args_checked(format_str, args...)});
 | 
			
		||||
  return vformat(ts, to_string_view(format_str),
 | 
			
		||||
                 {internal::make_args_checked<Args...>(format_str, args...)});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
FMT_END_NAMESPACE
 | 
			
		||||
 
 | 
			
		||||
@@ -14,250 +14,44 @@
 | 
			
		||||
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 {
 | 
			
		||||
 public:
 | 
			
		||||
  struct named_argument_id {
 | 
			
		||||
    FMT_CONSTEXPR named_argument_id(internal::string_view_metadata id)
 | 
			
		||||
        : id(id) {}
 | 
			
		||||
    internal::string_view_metadata id;
 | 
			
		||||
  enum class kind { arg_index, arg_name, text, replacement };
 | 
			
		||||
 | 
			
		||||
  struct replacement {
 | 
			
		||||
    arg_ref<Char> arg_id;
 | 
			
		||||
    dynamic_format_specs<Char> specs;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  struct argument_id {
 | 
			
		||||
    FMT_CONSTEXPR argument_id() : argument_id(0u) {}
 | 
			
		||||
 | 
			
		||||
    FMT_CONSTEXPR argument_id(unsigned id)
 | 
			
		||||
        : which(which_arg_id::index), val(id) {}
 | 
			
		||||
 | 
			
		||||
    FMT_CONSTEXPR argument_id(internal::string_view_metadata id)
 | 
			
		||||
        : which(which_arg_id::named_index), val(id) {}
 | 
			
		||||
 | 
			
		||||
    enum class which_arg_id { index, named_index };
 | 
			
		||||
 | 
			
		||||
    which_arg_id which;
 | 
			
		||||
 | 
			
		||||
    union value {
 | 
			
		||||
      FMT_CONSTEXPR value() : index(0u) {}
 | 
			
		||||
      FMT_CONSTEXPR value(unsigned id) : index(id) {}
 | 
			
		||||
      FMT_CONSTEXPR value(internal::string_view_metadata id)
 | 
			
		||||
          : named_index(id) {}
 | 
			
		||||
 | 
			
		||||
      unsigned index;
 | 
			
		||||
      internal::string_view_metadata named_index;
 | 
			
		||||
    } val;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  struct specification {
 | 
			
		||||
    FMT_CONSTEXPR specification() : arg_id(0u) {}
 | 
			
		||||
    FMT_CONSTEXPR specification(unsigned id) : arg_id(id) {}
 | 
			
		||||
 | 
			
		||||
    FMT_CONSTEXPR specification(internal::string_view_metadata id)
 | 
			
		||||
        : arg_id(id) {}
 | 
			
		||||
 | 
			
		||||
    argument_id arg_id;
 | 
			
		||||
    internal::dynamic_format_specs<Char> parsed_specs;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  FMT_CONSTEXPR format_part()
 | 
			
		||||
      : which(kind::argument_id), end_of_argument_id(0u), val(0u) {}
 | 
			
		||||
 | 
			
		||||
  FMT_CONSTEXPR format_part(internal::string_view_metadata text)
 | 
			
		||||
      : which(kind::text), end_of_argument_id(0u), val(text) {}
 | 
			
		||||
 | 
			
		||||
  FMT_CONSTEXPR format_part(unsigned id)
 | 
			
		||||
      : which(kind::argument_id), end_of_argument_id(0u), val(id) {}
 | 
			
		||||
 | 
			
		||||
  FMT_CONSTEXPR format_part(named_argument_id arg_id)
 | 
			
		||||
      : which(kind::named_argument_id), end_of_argument_id(0u), val(arg_id) {}
 | 
			
		||||
 | 
			
		||||
  FMT_CONSTEXPR format_part(specification spec)
 | 
			
		||||
      : which(kind::specification), end_of_argument_id(0u), val(spec) {}
 | 
			
		||||
 | 
			
		||||
  enum class kind { argument_id, named_argument_id, text, specification };
 | 
			
		||||
 | 
			
		||||
  kind which;
 | 
			
		||||
  std::size_t end_of_argument_id;
 | 
			
		||||
  kind part_kind;
 | 
			
		||||
  union value {
 | 
			
		||||
    FMT_CONSTEXPR value() : arg_id(0u) {}
 | 
			
		||||
    FMT_CONSTEXPR value(unsigned id) : arg_id(id) {}
 | 
			
		||||
    FMT_CONSTEXPR value(named_argument_id named_id)
 | 
			
		||||
        : named_arg_id(named_id.id) {}
 | 
			
		||||
    FMT_CONSTEXPR value(internal::string_view_metadata t) : text(t) {}
 | 
			
		||||
    FMT_CONSTEXPR value(specification s) : spec(s) {}
 | 
			
		||||
    unsigned arg_id;
 | 
			
		||||
    internal::string_view_metadata named_arg_id;
 | 
			
		||||
    internal::string_view_metadata text;
 | 
			
		||||
    specification spec;
 | 
			
		||||
    unsigned arg_index;
 | 
			
		||||
    basic_string_view<Char> str;
 | 
			
		||||
    replacement repl;
 | 
			
		||||
 | 
			
		||||
    FMT_CONSTEXPR value(unsigned 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;
 | 
			
		||||
 | 
			
		||||
template <typename Char, typename PartsContainer>
 | 
			
		||||
class format_preparation_handler : public internal::error_handler {
 | 
			
		||||
 private:
 | 
			
		||||
  using part = format_part<Char>;
 | 
			
		||||
  FMT_CONSTEXPR format_part(kind k = kind::arg_index, value v = {})
 | 
			
		||||
      : part_kind(k), val(v) {}
 | 
			
		||||
 | 
			
		||||
 public:
 | 
			
		||||
  using iterator = typename basic_string_view<Char>::iterator;
 | 
			
		||||
 | 
			
		||||
  FMT_CONSTEXPR format_preparation_handler(basic_string_view<Char> format,
 | 
			
		||||
                                           PartsContainer& parts)
 | 
			
		||||
      : parts_(parts), format_(format), parse_context_(format) {}
 | 
			
		||||
 | 
			
		||||
  FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
 | 
			
		||||
    if (begin == end) return;
 | 
			
		||||
    const auto offset = begin - format_.data();
 | 
			
		||||
    const auto size = end - begin;
 | 
			
		||||
    parts_.push_back(part(string_view_metadata(offset, size)));
 | 
			
		||||
  static FMT_CONSTEXPR format_part make_arg_index(unsigned index) {
 | 
			
		||||
    return format_part(kind::arg_index, index);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  FMT_CONSTEXPR void on_arg_id() {
 | 
			
		||||
    parts_.push_back(part(parse_context_.next_arg_id()));
 | 
			
		||||
  static FMT_CONSTEXPR format_part make_arg_name(basic_string_view<Char> name) {
 | 
			
		||||
    return format_part(kind::arg_name, name);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  FMT_CONSTEXPR void on_arg_id(unsigned id) {
 | 
			
		||||
    parse_context_.check_arg_id(id);
 | 
			
		||||
    parts_.push_back(part(id));
 | 
			
		||||
  static FMT_CONSTEXPR format_part make_text(basic_string_view<Char> text) {
 | 
			
		||||
    return format_part(kind::text, text);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  FMT_CONSTEXPR void on_arg_id(basic_string_view<Char> id) {
 | 
			
		||||
    const auto view = string_view_metadata(format_, id);
 | 
			
		||||
    const auto arg_id = typename part::named_argument_id(view);
 | 
			
		||||
    parts_.push_back(part(arg_id));
 | 
			
		||||
  static FMT_CONSTEXPR format_part make_replacement(replacement repl) {
 | 
			
		||||
    return format_part(kind::replacement, repl);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  FMT_CONSTEXPR void on_replacement_field(const Char* ptr) {
 | 
			
		||||
    parts_.back().end_of_argument_id = ptr - format_.begin();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  FMT_CONSTEXPR const Char* on_format_specs(const Char* begin,
 | 
			
		||||
                                            const Char* end) {
 | 
			
		||||
    const auto specs_offset = to_unsigned(begin - format_.begin());
 | 
			
		||||
 | 
			
		||||
    using parse_context = basic_parse_context<Char>;
 | 
			
		||||
    internal::dynamic_format_specs<Char> parsed_specs;
 | 
			
		||||
    dynamic_specs_handler<parse_context> handler(parsed_specs, parse_context_);
 | 
			
		||||
    begin = parse_format_specs(begin, end, handler);
 | 
			
		||||
 | 
			
		||||
    if (*begin != '}') on_error("missing '}' in format string");
 | 
			
		||||
 | 
			
		||||
    auto& last_part = parts_.back();
 | 
			
		||||
    auto specs = last_part.which == part::kind::argument_id
 | 
			
		||||
                     ? typename part::specification(last_part.val.arg_id)
 | 
			
		||||
                     : typename part::specification(last_part.val.named_arg_id);
 | 
			
		||||
    specs.parsed_specs = parsed_specs;
 | 
			
		||||
    last_part = part(specs);
 | 
			
		||||
    last_part.end_of_argument_id = specs_offset;
 | 
			
		||||
    return begin;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 private:
 | 
			
		||||
  PartsContainer& parts_;
 | 
			
		||||
  basic_string_view<Char> format_;
 | 
			
		||||
  basic_parse_context<Char> parse_context_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template <typename Format, typename PreparedPartsProvider, typename... Args>
 | 
			
		||||
class prepared_format {
 | 
			
		||||
 public:
 | 
			
		||||
  using char_type = char_t<Format>;
 | 
			
		||||
  using format_part_t = format_part<char_type>;
 | 
			
		||||
 | 
			
		||||
  constexpr prepared_format(Format f)
 | 
			
		||||
      : format_(std::move(f)), parts_provider_(to_string_view(format_)) {}
 | 
			
		||||
 | 
			
		||||
  prepared_format() = delete;
 | 
			
		||||
 | 
			
		||||
  using context = buffer_context<char_type>;
 | 
			
		||||
 | 
			
		||||
  template <typename Range, typename Context>
 | 
			
		||||
  auto vformat_to(Range out, basic_format_args<Context> args) const ->
 | 
			
		||||
      typename Context::iterator {
 | 
			
		||||
    const auto format_view = internal::to_string_view(format_);
 | 
			
		||||
    basic_parse_context<char_type> parse_ctx(format_view);
 | 
			
		||||
    Context ctx(out.begin(), args);
 | 
			
		||||
 | 
			
		||||
    const auto& parts = parts_provider_.parts();
 | 
			
		||||
    for (auto part_it = parts.begin(); part_it != parts.end(); ++part_it) {
 | 
			
		||||
      const auto& part = *part_it;
 | 
			
		||||
      const auto& value = part.val;
 | 
			
		||||
 | 
			
		||||
      switch (part.which) {
 | 
			
		||||
      case format_part_t::kind::text: {
 | 
			
		||||
        const auto text = value.text.to_view(format_view.data());
 | 
			
		||||
        auto output = ctx.out();
 | 
			
		||||
        auto&& it = internal::reserve(output, text.size());
 | 
			
		||||
        it = std::copy_n(text.begin(), text.size(), it);
 | 
			
		||||
        ctx.advance_to(output);
 | 
			
		||||
      } break;
 | 
			
		||||
 | 
			
		||||
      case format_part_t::kind::argument_id: {
 | 
			
		||||
        advance_parse_context_to_specification(parse_ctx, part);
 | 
			
		||||
        format_arg<Range>(parse_ctx, ctx, value.arg_id);
 | 
			
		||||
      } break;
 | 
			
		||||
 | 
			
		||||
      case format_part_t::kind::named_argument_id: {
 | 
			
		||||
        advance_parse_context_to_specification(parse_ctx, part);
 | 
			
		||||
        const auto named_arg_id =
 | 
			
		||||
            value.named_arg_id.to_view(format_view.data());
 | 
			
		||||
        format_arg<Range>(parse_ctx, ctx, named_arg_id);
 | 
			
		||||
      } break;
 | 
			
		||||
      case format_part_t::kind::specification: {
 | 
			
		||||
        const auto& arg_id_value = value.spec.arg_id.val;
 | 
			
		||||
        const auto arg = value.spec.arg_id.which ==
 | 
			
		||||
                                 format_part_t::argument_id::which_arg_id::index
 | 
			
		||||
                             ? ctx.arg(arg_id_value.index)
 | 
			
		||||
                             : ctx.arg(arg_id_value.named_index.to_view(
 | 
			
		||||
                                   to_string_view(format_).data()));
 | 
			
		||||
 | 
			
		||||
        auto specs = value.spec.parsed_specs;
 | 
			
		||||
 | 
			
		||||
        handle_dynamic_spec<internal::width_checker>(
 | 
			
		||||
            specs.width, specs.width_ref, ctx, format_view.begin());
 | 
			
		||||
        handle_dynamic_spec<internal::precision_checker>(
 | 
			
		||||
            specs.precision, specs.precision_ref, ctx, format_view.begin());
 | 
			
		||||
 | 
			
		||||
        check_prepared_specs(specs, arg.type());
 | 
			
		||||
        advance_parse_context_to_specification(parse_ctx, part);
 | 
			
		||||
        ctx.advance_to(
 | 
			
		||||
            visit_format_arg(arg_formatter<Range>(ctx, nullptr, &specs), arg));
 | 
			
		||||
      } break;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return ctx.out();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 private:
 | 
			
		||||
  void advance_parse_context_to_specification(
 | 
			
		||||
      basic_parse_context<char_type>& parse_ctx,
 | 
			
		||||
      const format_part_t& part) const {
 | 
			
		||||
    const auto view = to_string_view(format_);
 | 
			
		||||
    const auto specification_begin = view.data() + part.end_of_argument_id;
 | 
			
		||||
    advance_to(parse_ctx, specification_begin);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  template <typename Range, typename Context, typename Id>
 | 
			
		||||
  void format_arg(basic_parse_context<char_type>& parse_ctx, Context& ctx,
 | 
			
		||||
                  Id arg_id) const {
 | 
			
		||||
    parse_ctx.check_arg_id(arg_id);
 | 
			
		||||
    const auto stopped_at =
 | 
			
		||||
        visit_format_arg(arg_formatter<Range>(ctx), ctx.arg(arg_id));
 | 
			
		||||
    ctx.advance_to(stopped_at);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  template <typename Char>
 | 
			
		||||
  void check_prepared_specs(const basic_format_specs<Char>& specs,
 | 
			
		||||
                            internal::type arg_type) const {
 | 
			
		||||
    internal::error_handler h;
 | 
			
		||||
    numeric_specs_checker<internal::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();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 private:
 | 
			
		||||
  Format format_;
 | 
			
		||||
  PreparedPartsProvider parts_provider_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template <typename Char> struct part_counter {
 | 
			
		||||
@@ -276,13 +70,13 @@ template <typename Char> struct part_counter {
 | 
			
		||||
  FMT_CONSTEXPR const Char* on_format_specs(const Char* begin,
 | 
			
		||||
                                            const Char* end) {
 | 
			
		||||
    // Find the matching brace.
 | 
			
		||||
    unsigned braces_counter = 0;
 | 
			
		||||
    unsigned brace_counter = 0;
 | 
			
		||||
    for (; begin != end; ++begin) {
 | 
			
		||||
      if (*begin == '{') {
 | 
			
		||||
        ++braces_counter;
 | 
			
		||||
        ++brace_counter;
 | 
			
		||||
      } else if (*begin == '}') {
 | 
			
		||||
        if (braces_counter == 0u) break;
 | 
			
		||||
        --braces_counter;
 | 
			
		||||
        if (brace_counter == 0u) break;
 | 
			
		||||
        --brace_counter;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return begin;
 | 
			
		||||
@@ -291,156 +85,486 @@ template <typename Char> struct part_counter {
 | 
			
		||||
  FMT_CONSTEXPR void on_error(const char*) {}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template <typename Format> class compiletime_prepared_parts_type_provider {
 | 
			
		||||
 private:
 | 
			
		||||
  using char_type = char_t<Format>;
 | 
			
		||||
// 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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  static FMT_CONSTEXPR unsigned count_parts() {
 | 
			
		||||
    FMT_CONSTEXPR_DECL const auto text = to_string_view(Format{});
 | 
			
		||||
    part_counter<char_type> counter;
 | 
			
		||||
    internal::parse_format_string</*IS_CONSTEXPR=*/true>(text, 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)}));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
// Workaround for old compilers. Compiletime parts preparation will not be
 | 
			
		||||
// performed with them anyway.
 | 
			
		||||
  FMT_CONSTEXPR void on_arg_id() {
 | 
			
		||||
    part_ = part::make_arg_index(parse_context_.next_arg_id());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  FMT_CONSTEXPR void on_arg_id(unsigned 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 number_of_format_parts =
 | 
			
		||||
      compiletime_prepared_parts_type_provider::count_parts();
 | 
			
		||||
  static FMT_CONSTEXPR_DECL const unsigned num_format_parts =
 | 
			
		||||
      constexpr_max(count_parts(to_string_view(S())), 1u);
 | 
			
		||||
#else
 | 
			
		||||
  static const unsigned number_of_format_parts = 0u;
 | 
			
		||||
  static const unsigned num_format_parts = 1;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 public:
 | 
			
		||||
  template <unsigned N> struct format_parts_array {
 | 
			
		||||
    using value_type = format_part<char_type>;
 | 
			
		||||
  using parts_container = format_part<char_type>[num_format_parts];
 | 
			
		||||
 | 
			
		||||
    FMT_CONSTEXPR format_parts_array() : arr{} {}
 | 
			
		||||
 | 
			
		||||
    FMT_CONSTEXPR value_type& operator[](unsigned ind) { return arr[ind]; }
 | 
			
		||||
 | 
			
		||||
    FMT_CONSTEXPR const value_type* begin() const { return arr; }
 | 
			
		||||
    FMT_CONSTEXPR const value_type* end() const { return begin() + N; }
 | 
			
		||||
 | 
			
		||||
   private:
 | 
			
		||||
    value_type arr[N];
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  struct empty {
 | 
			
		||||
    // Parts preparator will search for it
 | 
			
		||||
    using value_type = format_part<char_type>;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  using type = conditional_t<number_of_format_parts != 0,
 | 
			
		||||
                             format_parts_array<number_of_format_parts>, empty>;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template <typename Parts> class compiletime_prepared_parts_collector {
 | 
			
		||||
 private:
 | 
			
		||||
  using format_part = typename Parts::value_type;
 | 
			
		||||
 | 
			
		||||
 public:
 | 
			
		||||
  FMT_CONSTEXPR explicit compiletime_prepared_parts_collector(Parts& parts)
 | 
			
		||||
      : parts_{parts}, counter_{0u} {}
 | 
			
		||||
 | 
			
		||||
  FMT_CONSTEXPR void push_back(format_part part) { parts_[counter_++] = part; }
 | 
			
		||||
 | 
			
		||||
  FMT_CONSTEXPR format_part& back() { return parts_[counter_ - 1]; }
 | 
			
		||||
 | 
			
		||||
 private:
 | 
			
		||||
  Parts& parts_;
 | 
			
		||||
  unsigned counter_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template <typename PartsContainer, typename Char>
 | 
			
		||||
FMT_CONSTEXPR PartsContainer prepare_parts(basic_string_view<Char> format) {
 | 
			
		||||
  PartsContainer parts;
 | 
			
		||||
  internal::parse_format_string</*IS_CONSTEXPR=*/false>(
 | 
			
		||||
      format, format_preparation_handler<Char, PartsContainer>(format, parts));
 | 
			
		||||
  return parts;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename PartsContainer, typename Char>
 | 
			
		||||
FMT_CONSTEXPR PartsContainer
 | 
			
		||||
prepare_compiletime_parts(basic_string_view<Char> format) {
 | 
			
		||||
  using collector = compiletime_prepared_parts_collector<PartsContainer>;
 | 
			
		||||
 | 
			
		||||
  PartsContainer parts;
 | 
			
		||||
  collector c(parts);
 | 
			
		||||
  internal::parse_format_string</*IS_CONSTEXPR=*/true>(
 | 
			
		||||
      format, format_preparation_handler<Char, collector>(format, c));
 | 
			
		||||
  return parts;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename PartsContainer> class runtime_parts_provider {
 | 
			
		||||
 public:
 | 
			
		||||
  runtime_parts_provider() = delete;
 | 
			
		||||
  template <typename Char>
 | 
			
		||||
  runtime_parts_provider(basic_string_view<Char> format)
 | 
			
		||||
      : parts_(prepare_parts<PartsContainer>(format)) {}
 | 
			
		||||
 | 
			
		||||
  const PartsContainer& parts() const { return parts_; }
 | 
			
		||||
 | 
			
		||||
 private:
 | 
			
		||||
  PartsContainer parts_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template <typename Format, typename PartsContainer>
 | 
			
		||||
struct compiletime_parts_provider {
 | 
			
		||||
  compiletime_parts_provider() = delete;
 | 
			
		||||
  template <typename Char>
 | 
			
		||||
  FMT_CONSTEXPR compiletime_parts_provider(basic_string_view<Char>) {}
 | 
			
		||||
 | 
			
		||||
  const PartsContainer& parts() const {
 | 
			
		||||
    static FMT_CONSTEXPR_DECL const PartsContainer prepared_parts =
 | 
			
		||||
        prepare_compiletime_parts<PartsContainer>(
 | 
			
		||||
            internal::to_string_view(Format{}));
 | 
			
		||||
 | 
			
		||||
    return prepared_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 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>
 | 
			
		||||
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 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>
 | 
			
		||||
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)>
 | 
			
		||||
FMT_CONSTEXPR auto compile(S format_str) -> internal::prepared_format<
 | 
			
		||||
    S,
 | 
			
		||||
    internal::compiletime_parts_provider<
 | 
			
		||||
        S,
 | 
			
		||||
        typename internal::compiletime_prepared_parts_type_provider<S>::type>,
 | 
			
		||||
    Args...> {
 | 
			
		||||
  return format_str;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
template <typename... Args, typename Char, size_t N>
 | 
			
		||||
auto compile(const Char (&format_str)[N]) -> internal::prepared_format<
 | 
			
		||||
    std::basic_string<Char>,
 | 
			
		||||
    internal::runtime_parts_provider<std::vector<internal::format_part<Char>>>,
 | 
			
		||||
    Args...> {
 | 
			
		||||
  return std::basic_string<Char>(format_str, N - 1);
 | 
			
		||||
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>
 | 
			
		||||
          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 = internal::buffer_range<Char>;
 | 
			
		||||
  using range = buffer_range<Char>;
 | 
			
		||||
  using context = buffer_context<Char>;
 | 
			
		||||
  cf.template vformat_to<range, context>(range(buffer),
 | 
			
		||||
                                         {make_format_args<context>(args...)});
 | 
			
		||||
  cf.format(std::back_inserter(buffer), args...);
 | 
			
		||||
  return to_string(buffer);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename OutputIt, typename CompiledFormat, typename... Args>
 | 
			
		||||
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) {
 | 
			
		||||
  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 cf.template vformat_to<range, context>(
 | 
			
		||||
      range(out), {make_format_args<context>(args...)});
 | 
			
		||||
  return internal::cf::vformat_to<context>(
 | 
			
		||||
      range(out), cf, {make_format_args<context>(args...)});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename OutputIt, typename CompiledFormat, typename... Args,
 | 
			
		||||
@@ -455,10 +579,7 @@ format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
 | 
			
		||||
 | 
			
		||||
template <typename CompiledFormat, typename... Args>
 | 
			
		||||
std::size_t formatted_size(const CompiledFormat& cf, const Args&... args) {
 | 
			
		||||
  return fmt::format_to(
 | 
			
		||||
             internal::counting_iterator<typename CompiledFormat::char_type>(),
 | 
			
		||||
             cf, args...)
 | 
			
		||||
      .count();
 | 
			
		||||
  return format_to(internal::counting_iterator(), cf, args...).count();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
FMT_END_NAMESPACE
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										247
									
								
								third_party/spdlog/include/spdlog/fmt/bundled/core.h
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										247
									
								
								third_party/spdlog/include/spdlog/fmt/bundled/core.h
									
									
									
									
										vendored
									
									
								
							@@ -8,7 +8,6 @@
 | 
			
		||||
#ifndef FMT_CORE_H_
 | 
			
		||||
#define FMT_CORE_H_
 | 
			
		||||
 | 
			
		||||
#include <cassert>
 | 
			
		||||
#include <cstdio>  // std::FILE
 | 
			
		||||
#include <cstring>
 | 
			
		||||
#include <iterator>
 | 
			
		||||
@@ -16,7 +15,7 @@
 | 
			
		||||
#include <type_traits>
 | 
			
		||||
 | 
			
		||||
// The fmt library version in the form major * 10000 + minor * 100 + patch.
 | 
			
		||||
#define FMT_VERSION 60000
 | 
			
		||||
#define FMT_VERSION 60101
 | 
			
		||||
 | 
			
		||||
#ifdef __has_feature
 | 
			
		||||
#  define FMT_HAS_FEATURE(x) __has_feature(x)
 | 
			
		||||
@@ -49,6 +48,12 @@
 | 
			
		||||
#  define FMT_HAS_GXX_CXX11 0
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef __NVCC__
 | 
			
		||||
#  define FMT_NVCC __NVCC__
 | 
			
		||||
#else
 | 
			
		||||
#  define FMT_NVCC 0
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef _MSC_VER
 | 
			
		||||
#  define FMT_MSC_VER _MSC_VER
 | 
			
		||||
#else
 | 
			
		||||
@@ -60,7 +65,8 @@
 | 
			
		||||
#ifndef FMT_USE_CONSTEXPR
 | 
			
		||||
#  define FMT_USE_CONSTEXPR                                           \
 | 
			
		||||
    (FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VER >= 1910 || \
 | 
			
		||||
     (FMT_GCC_VERSION >= 600 && __cplusplus >= 201402L))
 | 
			
		||||
     (FMT_GCC_VERSION >= 600 && __cplusplus >= 201402L)) &&           \
 | 
			
		||||
        !FMT_NVCC
 | 
			
		||||
#endif
 | 
			
		||||
#if FMT_USE_CONSTEXPR
 | 
			
		||||
#  define FMT_CONSTEXPR constexpr
 | 
			
		||||
@@ -133,6 +139,13 @@
 | 
			
		||||
#  endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
// Workaround broken [[deprecated]] in the Intel compiler and NVCC.
 | 
			
		||||
#if defined(__INTEL_COMPILER) || FMT_NVCC
 | 
			
		||||
#  define FMT_DEPRECATED_ALIAS
 | 
			
		||||
#else
 | 
			
		||||
#  define FMT_DEPRECATED_ALIAS FMT_DEPRECATED
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifndef FMT_BEGIN_NAMESPACE
 | 
			
		||||
#  if FMT_HAS_FEATURE(cxx_inline_namespaces) || FMT_GCC_VERSION >= 404 || \
 | 
			
		||||
      FMT_MSC_VER >= 1900
 | 
			
		||||
@@ -173,10 +186,6 @@
 | 
			
		||||
#  define FMT_EXTERN
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifndef FMT_ASSERT
 | 
			
		||||
#  define FMT_ASSERT(condition, message) assert((condition) && message)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
// libc++ supports string_view in pre-c++17.
 | 
			
		||||
#if (FMT_HAS_INCLUDE(<string_view>) &&                       \
 | 
			
		||||
     (__cplusplus > 201402L || defined(_LIBCPP_VERSION))) || \
 | 
			
		||||
@@ -200,6 +209,8 @@ template <typename T>
 | 
			
		||||
using remove_reference_t = typename std::remove_reference<T>::type;
 | 
			
		||||
template <typename T>
 | 
			
		||||
using remove_const_t = typename std::remove_const<T>::type;
 | 
			
		||||
template <typename T>
 | 
			
		||||
using remove_cvref_t = typename std::remove_cv<remove_reference_t<T>>::type;
 | 
			
		||||
 | 
			
		||||
struct monostate {};
 | 
			
		||||
 | 
			
		||||
@@ -213,6 +224,19 @@ namespace internal {
 | 
			
		||||
// A workaround for gcc 4.8 to make void_t work in a SFINAE context.
 | 
			
		||||
template <typename... Ts> struct void_t_impl { using type = void; };
 | 
			
		||||
 | 
			
		||||
FMT_API void assert_fail(const char* file, int line, const char* message);
 | 
			
		||||
 | 
			
		||||
#ifndef FMT_ASSERT
 | 
			
		||||
#  ifdef NDEBUG
 | 
			
		||||
#    define FMT_ASSERT(condition, message)
 | 
			
		||||
#  else
 | 
			
		||||
#    define FMT_ASSERT(condition, message) \
 | 
			
		||||
      ((condition)                         \
 | 
			
		||||
           ? void()                        \
 | 
			
		||||
           : fmt::internal::assert_fail(__FILE__, __LINE__, (message)))
 | 
			
		||||
#  endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if defined(FMT_USE_STRING_VIEW)
 | 
			
		||||
template <typename Char> using std_string_view = std::basic_string_view<Char>;
 | 
			
		||||
#elif defined(FMT_USE_EXPERIMENTAL_STRING_VIEW)
 | 
			
		||||
@@ -222,7 +246,21 @@ using std_string_view = std::experimental::basic_string_view<Char>;
 | 
			
		||||
template <typename T> struct std_string_view {};
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
// Casts nonnegative integer to unsigned.
 | 
			
		||||
#ifdef FMT_USE_INT128
 | 
			
		||||
// Do nothing.
 | 
			
		||||
#elif defined(__SIZEOF_INT128__)
 | 
			
		||||
#  define FMT_USE_INT128 1
 | 
			
		||||
using int128_t = __int128_t;
 | 
			
		||||
using uint128_t = __uint128_t;
 | 
			
		||||
#else
 | 
			
		||||
#  define FMT_USE_INT128 0
 | 
			
		||||
#endif
 | 
			
		||||
#if !FMT_USE_INT128
 | 
			
		||||
struct int128_t {};
 | 
			
		||||
struct uint128_t {};
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
// Casts a nonnegative integer to unsigned.
 | 
			
		||||
template <typename Int>
 | 
			
		||||
FMT_CONSTEXPR typename std::make_unsigned<Int>::type to_unsigned(Int value) {
 | 
			
		||||
  FMT_ASSERT(value >= 0, "negative value");
 | 
			
		||||
@@ -266,10 +304,11 @@ template <typename Char> class basic_string_view {
 | 
			
		||||
      : data_(s), size_(std::char_traits<Char>::length(s)) {}
 | 
			
		||||
 | 
			
		||||
  /** Constructs a string reference from a ``std::basic_string`` object. */
 | 
			
		||||
  template <typename Alloc>
 | 
			
		||||
  FMT_CONSTEXPR basic_string_view(const std::basic_string<Char, Alloc>& s)
 | 
			
		||||
      FMT_NOEXCEPT : data_(s.data()),
 | 
			
		||||
                     size_(s.size()) {}
 | 
			
		||||
  template <typename Traits, typename Alloc>
 | 
			
		||||
  FMT_CONSTEXPR basic_string_view(
 | 
			
		||||
      const std::basic_string<Char, Traits, Alloc>& s) FMT_NOEXCEPT
 | 
			
		||||
      : data_(s.data()),
 | 
			
		||||
        size_(s.size()) {}
 | 
			
		||||
 | 
			
		||||
  template <
 | 
			
		||||
      typename S,
 | 
			
		||||
@@ -286,6 +325,8 @@ template <typename Char> class basic_string_view {
 | 
			
		||||
  FMT_CONSTEXPR iterator begin() const { return data_; }
 | 
			
		||||
  FMT_CONSTEXPR iterator end() const { return data_ + size_; }
 | 
			
		||||
 | 
			
		||||
  FMT_CONSTEXPR const Char& operator[](size_t pos) const { return data_[pos]; }
 | 
			
		||||
 | 
			
		||||
  FMT_CONSTEXPR void remove_prefix(size_t n) {
 | 
			
		||||
    data_ += n;
 | 
			
		||||
    size_ -= n;
 | 
			
		||||
@@ -357,10 +398,10 @@ inline basic_string_view<Char> to_string_view(const Char* s) {
 | 
			
		||||
  return s;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename Char, typename Traits, typename Allocator>
 | 
			
		||||
template <typename Char, typename Traits, typename Alloc>
 | 
			
		||||
inline basic_string_view<Char> to_string_view(
 | 
			
		||||
    const std::basic_string<Char, Traits, Allocator>& s) {
 | 
			
		||||
  return {s.data(), s.size()};
 | 
			
		||||
    const std::basic_string<Char, Traits, Alloc>& s) {
 | 
			
		||||
  return s;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename Char>
 | 
			
		||||
@@ -405,8 +446,8 @@ template <typename S> struct char_t_impl<S, enable_if_t<is_string<S>::value>> {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct error_handler {
 | 
			
		||||
  FMT_CONSTEXPR error_handler() {}
 | 
			
		||||
  FMT_CONSTEXPR error_handler(const error_handler&) {}
 | 
			
		||||
  FMT_CONSTEXPR error_handler() = default;
 | 
			
		||||
  FMT_CONSTEXPR error_handler(const error_handler&) = default;
 | 
			
		||||
 | 
			
		||||
  // This function is intentionally not constexpr to give a compile-time error.
 | 
			
		||||
  FMT_NORETURN FMT_API void on_error(const char* message);
 | 
			
		||||
@@ -416,10 +457,24 @@ struct error_handler {
 | 
			
		||||
/** String's character type. */
 | 
			
		||||
template <typename S> using char_t = typename internal::char_t_impl<S>::type;
 | 
			
		||||
 | 
			
		||||
// Parsing context consisting of a format string range being parsed and an
 | 
			
		||||
// argument counter for automatic indexing.
 | 
			
		||||
/**
 | 
			
		||||
  \rst
 | 
			
		||||
  Parsing context consisting of a format string range being parsed and an
 | 
			
		||||
  argument counter for automatic indexing.
 | 
			
		||||
 | 
			
		||||
  You can use one of the following type aliases for common character types:
 | 
			
		||||
 | 
			
		||||
  +-----------------------+-------------------------------------+
 | 
			
		||||
  | Type                  | Definition                          |
 | 
			
		||||
  +=======================+=====================================+
 | 
			
		||||
  | format_parse_context  | basic_format_parse_context<char>    |
 | 
			
		||||
  +-----------------------+-------------------------------------+
 | 
			
		||||
  | wformat_parse_context | basic_format_parse_context<wchar_t> |
 | 
			
		||||
  +-----------------------+-------------------------------------+
 | 
			
		||||
  \endrst
 | 
			
		||||
 */
 | 
			
		||||
template <typename Char, typename ErrorHandler = internal::error_handler>
 | 
			
		||||
class basic_parse_context : private ErrorHandler {
 | 
			
		||||
class basic_format_parse_context : private ErrorHandler {
 | 
			
		||||
 private:
 | 
			
		||||
  basic_string_view<Char> format_str_;
 | 
			
		||||
  int next_arg_id_;
 | 
			
		||||
@@ -428,38 +483,47 @@ class basic_parse_context : private ErrorHandler {
 | 
			
		||||
  using char_type = Char;
 | 
			
		||||
  using iterator = typename basic_string_view<Char>::iterator;
 | 
			
		||||
 | 
			
		||||
  explicit FMT_CONSTEXPR basic_parse_context(basic_string_view<Char> format_str,
 | 
			
		||||
                                             ErrorHandler eh = ErrorHandler())
 | 
			
		||||
  explicit FMT_CONSTEXPR basic_format_parse_context(
 | 
			
		||||
      basic_string_view<Char> format_str, ErrorHandler eh = ErrorHandler())
 | 
			
		||||
      : ErrorHandler(eh), format_str_(format_str), next_arg_id_(0) {}
 | 
			
		||||
 | 
			
		||||
  // Returns an iterator to the beginning of the format string range being
 | 
			
		||||
  // parsed.
 | 
			
		||||
  /**
 | 
			
		||||
    Returns an iterator to the beginning of the format string range being
 | 
			
		||||
    parsed.
 | 
			
		||||
   */
 | 
			
		||||
  FMT_CONSTEXPR iterator begin() const FMT_NOEXCEPT {
 | 
			
		||||
    return format_str_.begin();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Returns an iterator past the end of the format string range being parsed.
 | 
			
		||||
  /**
 | 
			
		||||
    Returns an iterator past the end of the format string range being parsed.
 | 
			
		||||
   */
 | 
			
		||||
  FMT_CONSTEXPR iterator end() const FMT_NOEXCEPT { return format_str_.end(); }
 | 
			
		||||
 | 
			
		||||
  // Advances the begin iterator to ``it``.
 | 
			
		||||
  /** Advances the begin iterator to ``it``. */
 | 
			
		||||
  FMT_CONSTEXPR void advance_to(iterator it) {
 | 
			
		||||
    format_str_.remove_prefix(internal::to_unsigned(it - begin()));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Returns the next argument index.
 | 
			
		||||
  /**
 | 
			
		||||
    Reports an error if using the manual argument indexing; otherwise returns
 | 
			
		||||
    the next argument index and switches to the automatic indexing.
 | 
			
		||||
   */
 | 
			
		||||
  FMT_CONSTEXPR int next_arg_id() {
 | 
			
		||||
    if (next_arg_id_ >= 0) return next_arg_id_++;
 | 
			
		||||
    on_error("cannot switch from manual to automatic argument indexing");
 | 
			
		||||
    return 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  FMT_CONSTEXPR bool check_arg_id(int) {
 | 
			
		||||
    if (next_arg_id_ > 0) {
 | 
			
		||||
  /**
 | 
			
		||||
    Reports an error if using the automatic argument indexing; otherwise
 | 
			
		||||
    switches to the manual indexing.
 | 
			
		||||
   */
 | 
			
		||||
  FMT_CONSTEXPR void check_arg_id(int) {
 | 
			
		||||
    if (next_arg_id_ > 0)
 | 
			
		||||
      on_error("cannot switch from automatic to manual argument indexing");
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
    next_arg_id_ = -1;
 | 
			
		||||
    return true;
 | 
			
		||||
    else
 | 
			
		||||
      next_arg_id_ = -1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  FMT_CONSTEXPR void check_arg_id(basic_string_view<Char>) {}
 | 
			
		||||
@@ -471,11 +535,14 @@ class basic_parse_context : private ErrorHandler {
 | 
			
		||||
  FMT_CONSTEXPR ErrorHandler error_handler() const { return *this; }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
using format_parse_context = basic_parse_context<char>;
 | 
			
		||||
using wformat_parse_context = basic_parse_context<wchar_t>;
 | 
			
		||||
using format_parse_context = basic_format_parse_context<char>;
 | 
			
		||||
using wformat_parse_context = basic_format_parse_context<wchar_t>;
 | 
			
		||||
 | 
			
		||||
using parse_context FMT_DEPRECATED = basic_parse_context<char>;
 | 
			
		||||
using wparse_context FMT_DEPRECATED = basic_parse_context<wchar_t>;
 | 
			
		||||
template <typename Char, typename ErrorHandler = internal::error_handler>
 | 
			
		||||
using basic_parse_context FMT_DEPRECATED_ALIAS =
 | 
			
		||||
    basic_format_parse_context<Char, ErrorHandler>;
 | 
			
		||||
using parse_context FMT_DEPRECATED_ALIAS = basic_format_parse_context<char>;
 | 
			
		||||
using wparse_context FMT_DEPRECATED_ALIAS = basic_format_parse_context<wchar_t>;
 | 
			
		||||
 | 
			
		||||
template <typename Context> class basic_format_arg;
 | 
			
		||||
template <typename Context> class basic_format_args;
 | 
			
		||||
@@ -492,20 +559,17 @@ struct FMT_DEPRECATED convert_to_int
 | 
			
		||||
    : bool_constant<!std::is_arithmetic<T>::value &&
 | 
			
		||||
                    std::is_convertible<T, int>::value> {};
 | 
			
		||||
 | 
			
		||||
namespace internal {
 | 
			
		||||
 | 
			
		||||
// Specifies if T has an enabled formatter specialization. A type can be
 | 
			
		||||
// formattable even if it doesn't have a formatter e.g. via a conversion.
 | 
			
		||||
template <typename T, typename Context>
 | 
			
		||||
using has_formatter =
 | 
			
		||||
    std::is_constructible<typename Context::template formatter_type<T>>;
 | 
			
		||||
 | 
			
		||||
namespace internal {
 | 
			
		||||
 | 
			
		||||
/** A contiguous memory buffer with an optional growing ability. */
 | 
			
		||||
template <typename T> class buffer {
 | 
			
		||||
 private:
 | 
			
		||||
  buffer(const buffer&) = delete;
 | 
			
		||||
  void operator=(const buffer&) = delete;
 | 
			
		||||
 | 
			
		||||
  T* ptr_;
 | 
			
		||||
  std::size_t size_;
 | 
			
		||||
  std::size_t capacity_;
 | 
			
		||||
@@ -532,7 +596,9 @@ template <typename T> class buffer {
 | 
			
		||||
  using value_type = T;
 | 
			
		||||
  using const_reference = const T&;
 | 
			
		||||
 | 
			
		||||
  virtual ~buffer() {}
 | 
			
		||||
  buffer(const buffer&) = delete;
 | 
			
		||||
  void operator=(const buffer&) = delete;
 | 
			
		||||
  virtual ~buffer() = default;
 | 
			
		||||
 | 
			
		||||
  T* begin() FMT_NOEXCEPT { return ptr_; }
 | 
			
		||||
  T* end() FMT_NOEXCEPT { return ptr_ + size_; }
 | 
			
		||||
@@ -626,10 +692,13 @@ enum type {
 | 
			
		||||
  uint_type,
 | 
			
		||||
  long_long_type,
 | 
			
		||||
  ulong_long_type,
 | 
			
		||||
  int128_type,
 | 
			
		||||
  uint128_type,
 | 
			
		||||
  bool_type,
 | 
			
		||||
  char_type,
 | 
			
		||||
  last_integer_type = char_type,
 | 
			
		||||
  // followed by floating-point types.
 | 
			
		||||
  float_type,
 | 
			
		||||
  double_type,
 | 
			
		||||
  long_double_type,
 | 
			
		||||
  last_numeric_type = long_double_type,
 | 
			
		||||
@@ -652,20 +721,23 @@ FMT_TYPE_CONSTANT(int, int_type);
 | 
			
		||||
FMT_TYPE_CONSTANT(unsigned, uint_type);
 | 
			
		||||
FMT_TYPE_CONSTANT(long long, long_long_type);
 | 
			
		||||
FMT_TYPE_CONSTANT(unsigned long long, ulong_long_type);
 | 
			
		||||
FMT_TYPE_CONSTANT(int128_t, int128_type);
 | 
			
		||||
FMT_TYPE_CONSTANT(uint128_t, uint128_type);
 | 
			
		||||
FMT_TYPE_CONSTANT(bool, bool_type);
 | 
			
		||||
FMT_TYPE_CONSTANT(Char, char_type);
 | 
			
		||||
FMT_TYPE_CONSTANT(float, float_type);
 | 
			
		||||
FMT_TYPE_CONSTANT(double, double_type);
 | 
			
		||||
FMT_TYPE_CONSTANT(long double, long_double_type);
 | 
			
		||||
FMT_TYPE_CONSTANT(const Char*, cstring_type);
 | 
			
		||||
FMT_TYPE_CONSTANT(basic_string_view<Char>, string_type);
 | 
			
		||||
FMT_TYPE_CONSTANT(const void*, pointer_type);
 | 
			
		||||
 | 
			
		||||
FMT_CONSTEXPR bool is_integral(type t) {
 | 
			
		||||
FMT_CONSTEXPR bool is_integral_type(type t) {
 | 
			
		||||
  FMT_ASSERT(t != named_arg_type, "invalid argument type");
 | 
			
		||||
  return t > none_type && t <= last_integer_type;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
FMT_CONSTEXPR bool is_arithmetic(type t) {
 | 
			
		||||
FMT_CONSTEXPR bool is_arithmetic_type(type t) {
 | 
			
		||||
  FMT_ASSERT(t != named_arg_type, "invalid argument type");
 | 
			
		||||
  return t > none_type && t <= last_numeric_type;
 | 
			
		||||
}
 | 
			
		||||
@@ -676,7 +748,7 @@ template <typename Char> struct string_value {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template <typename Context> struct custom_value {
 | 
			
		||||
  using parse_context = basic_parse_context<typename Context::char_type>;
 | 
			
		||||
  using parse_context = basic_format_parse_context<typename Context::char_type>;
 | 
			
		||||
  const void* value;
 | 
			
		||||
  void (*format)(const void* arg, parse_context& parse_ctx, Context& ctx);
 | 
			
		||||
};
 | 
			
		||||
@@ -691,8 +763,11 @@ template <typename Context> class value {
 | 
			
		||||
    unsigned uint_value;
 | 
			
		||||
    long long long_long_value;
 | 
			
		||||
    unsigned long long ulong_long_value;
 | 
			
		||||
    int128_t int128_value;
 | 
			
		||||
    uint128_t uint128_value;
 | 
			
		||||
    bool bool_value;
 | 
			
		||||
    char_type char_value;
 | 
			
		||||
    float float_value;
 | 
			
		||||
    double double_value;
 | 
			
		||||
    long double long_double_value;
 | 
			
		||||
    const void* pointer;
 | 
			
		||||
@@ -705,6 +780,9 @@ template <typename Context> class value {
 | 
			
		||||
  FMT_CONSTEXPR value(unsigned val) : uint_value(val) {}
 | 
			
		||||
  value(long long val) : long_long_value(val) {}
 | 
			
		||||
  value(unsigned long long val) : ulong_long_value(val) {}
 | 
			
		||||
  value(int128_t val) : int128_value(val) {}
 | 
			
		||||
  value(uint128_t val) : uint128_value(val) {}
 | 
			
		||||
  value(float val) : float_value(val) {}
 | 
			
		||||
  value(double val) : double_value(val) {}
 | 
			
		||||
  value(long double val) : long_double_value(val) {}
 | 
			
		||||
  value(bool val) : bool_value(val) {}
 | 
			
		||||
@@ -732,9 +810,9 @@ template <typename Context> class value {
 | 
			
		||||
 private:
 | 
			
		||||
  // Formats an argument of a custom type, such as a user-defined class.
 | 
			
		||||
  template <typename T, typename Formatter>
 | 
			
		||||
  static void format_custom_arg(const void* arg,
 | 
			
		||||
                                basic_parse_context<char_type>& parse_ctx,
 | 
			
		||||
                                Context& ctx) {
 | 
			
		||||
  static void format_custom_arg(
 | 
			
		||||
      const void* arg, basic_format_parse_context<char_type>& parse_ctx,
 | 
			
		||||
      Context& ctx) {
 | 
			
		||||
    Formatter f;
 | 
			
		||||
    parse_ctx.advance_to(f.parse(parse_ctx));
 | 
			
		||||
    ctx.advance_to(f.format(*static_cast<const T*>(arg), ctx));
 | 
			
		||||
@@ -764,6 +842,8 @@ template <typename Context> struct arg_mapper {
 | 
			
		||||
  FMT_CONSTEXPR ulong_type map(unsigned long val) { return val; }
 | 
			
		||||
  FMT_CONSTEXPR long long map(long long val) { return val; }
 | 
			
		||||
  FMT_CONSTEXPR unsigned long long map(unsigned long long val) { return val; }
 | 
			
		||||
  FMT_CONSTEXPR int128_t map(int128_t val) { return val; }
 | 
			
		||||
  FMT_CONSTEXPR uint128_t map(uint128_t val) { return val; }
 | 
			
		||||
  FMT_CONSTEXPR bool map(bool val) { return val; }
 | 
			
		||||
 | 
			
		||||
  template <typename T, FMT_ENABLE_IF(is_char<T>::value)>
 | 
			
		||||
@@ -774,7 +854,7 @@ template <typename Context> struct arg_mapper {
 | 
			
		||||
    return val;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  FMT_CONSTEXPR double map(float val) { return static_cast<double>(val); }
 | 
			
		||||
  FMT_CONSTEXPR float map(float val) { return val; }
 | 
			
		||||
  FMT_CONSTEXPR double map(double val) { return val; }
 | 
			
		||||
  FMT_CONSTEXPR long double map(long double val) { return val; }
 | 
			
		||||
 | 
			
		||||
@@ -793,6 +873,15 @@ template <typename Context> struct arg_mapper {
 | 
			
		||||
  FMT_CONSTEXPR basic_string_view<char_type> map(const T& val) {
 | 
			
		||||
    return basic_string_view<char_type>(val);
 | 
			
		||||
  }
 | 
			
		||||
  template <
 | 
			
		||||
      typename T,
 | 
			
		||||
      FMT_ENABLE_IF(
 | 
			
		||||
          std::is_constructible<std_string_view<char_type>, T>::value &&
 | 
			
		||||
          !std::is_constructible<basic_string_view<char_type>, T>::value &&
 | 
			
		||||
          !is_string<T>::value)>
 | 
			
		||||
  FMT_CONSTEXPR basic_string_view<char_type> map(const T& val) {
 | 
			
		||||
    return std_string_view<char_type>(val);
 | 
			
		||||
  }
 | 
			
		||||
  FMT_CONSTEXPR const char* map(const signed char* val) {
 | 
			
		||||
    static_assert(std::is_same<char_type, char>::value, "invalid string type");
 | 
			
		||||
    return reinterpret_cast<const char*>(val);
 | 
			
		||||
@@ -818,11 +907,14 @@ template <typename Context> struct arg_mapper {
 | 
			
		||||
            FMT_ENABLE_IF(std::is_enum<T>::value &&
 | 
			
		||||
                          !has_formatter<T, Context>::value &&
 | 
			
		||||
                          !has_fallback_formatter<T, Context>::value)>
 | 
			
		||||
  FMT_CONSTEXPR int map(const T& val) {
 | 
			
		||||
    return static_cast<int>(val);
 | 
			
		||||
  FMT_CONSTEXPR auto map(const T& val) -> decltype(
 | 
			
		||||
      map(static_cast<typename std::underlying_type<T>::type>(val))) {
 | 
			
		||||
    return map(static_cast<typename std::underlying_type<T>::type>(val));
 | 
			
		||||
  }
 | 
			
		||||
  template <typename T,
 | 
			
		||||
            FMT_ENABLE_IF(!is_string<T>::value && !is_char<T>::value &&
 | 
			
		||||
                          !std::is_constructible<basic_string_view<char_type>,
 | 
			
		||||
                                                 T>::value &&
 | 
			
		||||
                          (has_formatter<T, Context>::value ||
 | 
			
		||||
                           has_fallback_formatter<T, Context>::value))>
 | 
			
		||||
  FMT_CONSTEXPR const T& map(const T& val) {
 | 
			
		||||
@@ -841,12 +933,13 @@ template <typename Context> struct arg_mapper {
 | 
			
		||||
// A type constant after applying arg_mapper<Context>.
 | 
			
		||||
template <typename T, typename Context>
 | 
			
		||||
using mapped_type_constant =
 | 
			
		||||
    type_constant<decltype(arg_mapper<Context>().map(std::declval<T>())),
 | 
			
		||||
    type_constant<decltype(arg_mapper<Context>().map(std::declval<const T&>())),
 | 
			
		||||
                  typename Context::char_type>;
 | 
			
		||||
 | 
			
		||||
enum { packed_arg_bits = 5 };
 | 
			
		||||
// Maximum number of arguments with packed types.
 | 
			
		||||
enum { max_packed_args = 15 };
 | 
			
		||||
enum : unsigned long long { is_unpacked_bit = 1ull << 63 };
 | 
			
		||||
enum { max_packed_args = 63 / packed_arg_bits };
 | 
			
		||||
enum : unsigned long long { is_unpacked_bit = 1ULL << 63 };
 | 
			
		||||
 | 
			
		||||
template <typename Context> class arg_map;
 | 
			
		||||
}  // namespace internal
 | 
			
		||||
@@ -877,7 +970,8 @@ template <typename Context> class basic_format_arg {
 | 
			
		||||
   public:
 | 
			
		||||
    explicit handle(internal::custom_value<Context> custom) : custom_(custom) {}
 | 
			
		||||
 | 
			
		||||
    void format(basic_parse_context<char_type>& parse_ctx, Context& ctx) const {
 | 
			
		||||
    void format(basic_format_parse_context<char_type>& parse_ctx,
 | 
			
		||||
                Context& ctx) const {
 | 
			
		||||
      custom_.format(custom_.value, parse_ctx, ctx);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -893,8 +987,8 @@ template <typename Context> class basic_format_arg {
 | 
			
		||||
 | 
			
		||||
  internal::type type() const { return type_; }
 | 
			
		||||
 | 
			
		||||
  bool is_integral() const { return internal::is_integral(type_); }
 | 
			
		||||
  bool is_arithmetic() const { return internal::is_arithmetic(type_); }
 | 
			
		||||
  bool is_integral() const { return internal::is_integral_type(type_); }
 | 
			
		||||
  bool is_arithmetic() const { return internal::is_arithmetic_type(type_); }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -923,10 +1017,22 @@ FMT_CONSTEXPR auto visit_format_arg(Visitor&& vis,
 | 
			
		||||
    return vis(arg.value_.long_long_value);
 | 
			
		||||
  case internal::ulong_long_type:
 | 
			
		||||
    return vis(arg.value_.ulong_long_value);
 | 
			
		||||
#if FMT_USE_INT128
 | 
			
		||||
  case internal::int128_type:
 | 
			
		||||
    return vis(arg.value_.int128_value);
 | 
			
		||||
  case internal::uint128_type:
 | 
			
		||||
    return vis(arg.value_.uint128_value);
 | 
			
		||||
#else
 | 
			
		||||
  case internal::int128_type:
 | 
			
		||||
  case internal::uint128_type:
 | 
			
		||||
    break;
 | 
			
		||||
#endif
 | 
			
		||||
  case internal::bool_type:
 | 
			
		||||
    return vis(arg.value_.bool_value);
 | 
			
		||||
  case internal::char_type:
 | 
			
		||||
    return vis(arg.value_.char_value);
 | 
			
		||||
  case internal::float_type:
 | 
			
		||||
    return vis(arg.value_.float_value);
 | 
			
		||||
  case internal::double_type:
 | 
			
		||||
    return vis(arg.value_.double_value);
 | 
			
		||||
  case internal::long_double_type:
 | 
			
		||||
@@ -948,9 +1054,6 @@ namespace internal {
 | 
			
		||||
// A map from argument names to their values for named arguments.
 | 
			
		||||
template <typename Context> class arg_map {
 | 
			
		||||
 private:
 | 
			
		||||
  arg_map(const arg_map&) = delete;
 | 
			
		||||
  void operator=(const arg_map&) = delete;
 | 
			
		||||
 | 
			
		||||
  using char_type = typename Context::char_type;
 | 
			
		||||
 | 
			
		||||
  struct entry {
 | 
			
		||||
@@ -968,6 +1071,8 @@ template <typename Context> class arg_map {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 public:
 | 
			
		||||
  arg_map(const arg_map&) = delete;
 | 
			
		||||
  void operator=(const arg_map&) = delete;
 | 
			
		||||
  arg_map() : map_(nullptr), size_(0) {}
 | 
			
		||||
  void init(const basic_format_args<Context>& args);
 | 
			
		||||
  ~arg_map() { delete[] map_; }
 | 
			
		||||
@@ -990,6 +1095,8 @@ class locale_ref {
 | 
			
		||||
  locale_ref() : locale_(nullptr) {}
 | 
			
		||||
  template <typename Locale> explicit locale_ref(const Locale& loc);
 | 
			
		||||
 | 
			
		||||
  explicit operator bool() const FMT_NOEXCEPT { return locale_ != nullptr; }
 | 
			
		||||
 | 
			
		||||
  template <typename Locale> Locale get() const;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -998,7 +1105,7 @@ template <typename> constexpr unsigned long long encode_types() { return 0; }
 | 
			
		||||
template <typename Context, typename Arg, typename... Args>
 | 
			
		||||
constexpr unsigned long long encode_types() {
 | 
			
		||||
  return mapped_type_constant<Arg, Context>::value |
 | 
			
		||||
         (encode_types<Context, Args...>() << 4);
 | 
			
		||||
         (encode_types<Context, Args...>() << packed_arg_bits);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename Context, typename T>
 | 
			
		||||
@@ -1034,14 +1141,13 @@ template <typename OutputIt, typename Char> class basic_format_context {
 | 
			
		||||
  internal::arg_map<basic_format_context> map_;
 | 
			
		||||
  internal::locale_ref loc_;
 | 
			
		||||
 | 
			
		||||
  basic_format_context(const basic_format_context&) = delete;
 | 
			
		||||
  void operator=(const basic_format_context&) = delete;
 | 
			
		||||
 | 
			
		||||
 public:
 | 
			
		||||
  using iterator = OutputIt;
 | 
			
		||||
  using format_arg = basic_format_arg<basic_format_context>;
 | 
			
		||||
  template <typename T> using formatter_type = formatter<T, char_type>;
 | 
			
		||||
 | 
			
		||||
  basic_format_context(const basic_format_context&) = delete;
 | 
			
		||||
  void operator=(const basic_format_context&) = delete;
 | 
			
		||||
  /**
 | 
			
		||||
   Constructs a ``basic_format_context`` object. References to the arguments are
 | 
			
		||||
   stored in the object so make sure they have appropriate lifetimes.
 | 
			
		||||
@@ -1100,7 +1206,6 @@ template <typename Context, typename... Args> class format_arg_store {
 | 
			
		||||
  static constexpr unsigned long long types =
 | 
			
		||||
      is_packed ? internal::encode_types<Context, Args...>()
 | 
			
		||||
                : internal::is_unpacked_bit | num_args;
 | 
			
		||||
  FMT_DEPRECATED static constexpr unsigned long long TYPES = types;
 | 
			
		||||
 | 
			
		||||
  format_arg_store(const Args&... args)
 | 
			
		||||
      : data_{internal::make_arg<is_packed, Context>(args)...} {}
 | 
			
		||||
@@ -1143,8 +1248,9 @@ template <typename Context> class basic_format_args {
 | 
			
		||||
  bool is_packed() const { return (types_ & internal::is_unpacked_bit) == 0; }
 | 
			
		||||
 | 
			
		||||
  internal::type type(int index) const {
 | 
			
		||||
    int shift = index * 4;
 | 
			
		||||
    return static_cast<internal::type>((types_ & (0xfull << shift)) >> shift);
 | 
			
		||||
    int shift = index * internal::packed_arg_bits;
 | 
			
		||||
    unsigned int mask = (1 << internal::packed_arg_bits) - 1;
 | 
			
		||||
    return static_cast<internal::type>((types_ >> shift) & mask);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  friend class internal::arg_map<Context>;
 | 
			
		||||
@@ -1371,7 +1477,7 @@ inline std::basic_string<Char> format(const S& format_str, Args&&... args) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
FMT_API void vprint(std::FILE* f, string_view format_str, format_args args);
 | 
			
		||||
FMT_API void vprint(std::FILE* f, wstring_view format_str, wformat_args args);
 | 
			
		||||
FMT_API void vprint(string_view format_str, format_args args);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
  \rst
 | 
			
		||||
@@ -1391,9 +1497,6 @@ inline void print(std::FILE* f, const S& format_str, Args&&... args) {
 | 
			
		||||
         internal::make_args_checked<Args...>(format_str, args...));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
FMT_API void vprint(string_view format_str, format_args args);
 | 
			
		||||
FMT_API void vprint(wstring_view format_str, wformat_args args);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
  \rst
 | 
			
		||||
  Prints formatted data to ``stdout``.
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										1192
									
								
								third_party/spdlog/include/spdlog/fmt/bundled/format.h
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1192
									
								
								third_party/spdlog/include/spdlog/fmt/bundled/format.h
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -46,9 +46,13 @@ template <class Char> class formatbuf : public std::basic_streambuf<Char> {
 | 
			
		||||
 | 
			
		||||
template <typename Char> struct test_stream : std::basic_ostream<Char> {
 | 
			
		||||
 private:
 | 
			
		||||
  struct null;
 | 
			
		||||
  // Hide all operator<< from std::basic_ostream<Char>.
 | 
			
		||||
  void operator<<(null);
 | 
			
		||||
  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
 | 
			
		||||
@@ -56,9 +60,9 @@ template <typename Char> struct test_stream : std::basic_ostream<Char> {
 | 
			
		||||
template <typename T, typename Char> class is_streamable {
 | 
			
		||||
 private:
 | 
			
		||||
  template <typename U>
 | 
			
		||||
  static decltype((void)(std::declval<test_stream<Char>&>()
 | 
			
		||||
                         << std::declval<U>()),
 | 
			
		||||
                  std::true_type())
 | 
			
		||||
  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(...);
 | 
			
		||||
@@ -75,8 +79,7 @@ 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((std::numeric_limits<std::streamsize>::max)());
 | 
			
		||||
  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));
 | 
			
		||||
@@ -86,9 +89,11 @@ void write(std::basic_ostream<Char>& os, buffer<Char>& buf) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename Char, typename T>
 | 
			
		||||
void format_value(buffer<Char>& buf, const T& value) {
 | 
			
		||||
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 (loc) output.imbue(loc.get<std::locale>());
 | 
			
		||||
  output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
 | 
			
		||||
  output << value;
 | 
			
		||||
  buf.resize(buf.size());
 | 
			
		||||
@@ -101,7 +106,7 @@ struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
 | 
			
		||||
  template <typename Context>
 | 
			
		||||
  auto format(const T& value, Context& ctx) -> decltype(ctx.out()) {
 | 
			
		||||
    basic_memory_buffer<Char> buffer;
 | 
			
		||||
    format_value(buffer, value);
 | 
			
		||||
    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);
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -13,11 +13,10 @@
 | 
			
		||||
#  undef __STRICT_ANSI__
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <fcntl.h>   // for O_RDONLY
 | 
			
		||||
#include <locale.h>  // for locale_t
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>  // for strtod_l
 | 
			
		||||
#include <cerrno>
 | 
			
		||||
#include <clocale>  // for locale_t
 | 
			
		||||
#include <cstdio>
 | 
			
		||||
#include <cstdlib>  // for strtod_l
 | 
			
		||||
 | 
			
		||||
#include <cstddef>
 | 
			
		||||
 | 
			
		||||
@@ -27,6 +26,18 @@
 | 
			
		||||
 | 
			
		||||
#include "format.h"
 | 
			
		||||
 | 
			
		||||
// UWP doesn't provide _pipe.
 | 
			
		||||
#if FMT_HAS_INCLUDE("winapifamily.h")
 | 
			
		||||
#  include <winapifamily.h>
 | 
			
		||||
#endif
 | 
			
		||||
#if FMT_HAS_INCLUDE("fcntl.h") && \
 | 
			
		||||
    (!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
 | 
			
		||||
#  include <fcntl.h>  // for O_RDONLY
 | 
			
		||||
#  define FMT_USE_FCNTL 1
 | 
			
		||||
#else
 | 
			
		||||
#  define FMT_USE_FCNTL 0
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifndef FMT_POSIX
 | 
			
		||||
#  if defined(_WIN32) && !defined(__MINGW32__)
 | 
			
		||||
// Fix warnings about deprecated symbols.
 | 
			
		||||
@@ -54,8 +65,8 @@
 | 
			
		||||
#ifndef _WIN32
 | 
			
		||||
#  define FMT_RETRY_VAL(result, expression, error_result) \
 | 
			
		||||
    do {                                                  \
 | 
			
		||||
      result = (expression);                              \
 | 
			
		||||
    } while (result == error_result && errno == EINTR)
 | 
			
		||||
      (result) = (expression);                            \
 | 
			
		||||
    } while ((result) == (error_result) && errno == EINTR)
 | 
			
		||||
#else
 | 
			
		||||
#  define FMT_RETRY_VAL(result, expression, error_result) result = (expression)
 | 
			
		||||
#endif
 | 
			
		||||
@@ -132,16 +143,15 @@ class buffered_file {
 | 
			
		||||
  explicit buffered_file(FILE* f) : file_(f) {}
 | 
			
		||||
 | 
			
		||||
 public:
 | 
			
		||||
  buffered_file(const buffered_file&) = delete;
 | 
			
		||||
  void operator=(const buffered_file&) = delete;
 | 
			
		||||
 | 
			
		||||
  // Constructs a buffered_file object which doesn't represent any file.
 | 
			
		||||
  buffered_file() FMT_NOEXCEPT : file_(nullptr) {}
 | 
			
		||||
 | 
			
		||||
  // Destroys the object closing the file it represents if any.
 | 
			
		||||
  FMT_API ~buffered_file() FMT_NOEXCEPT;
 | 
			
		||||
 | 
			
		||||
 private:
 | 
			
		||||
  buffered_file(const buffered_file&) = delete;
 | 
			
		||||
  void operator=(const buffered_file&) = delete;
 | 
			
		||||
 | 
			
		||||
 public:
 | 
			
		||||
  buffered_file(buffered_file&& other) FMT_NOEXCEPT : file_(other.file_) {
 | 
			
		||||
    other.file_ = nullptr;
 | 
			
		||||
@@ -177,6 +187,7 @@ class buffered_file {
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#if FMT_USE_FCNTL
 | 
			
		||||
// A file. Closed file is represented by a file object with descriptor -1.
 | 
			
		||||
// Methods that are not declared with FMT_NOEXCEPT may throw
 | 
			
		||||
// fmt::system_error in case of failure. Note that some errors such as
 | 
			
		||||
@@ -204,14 +215,13 @@ class file {
 | 
			
		||||
  // Opens a file and constructs a file object representing this file.
 | 
			
		||||
  FMT_API file(cstring_view path, int oflag);
 | 
			
		||||
 | 
			
		||||
 private:
 | 
			
		||||
 public:
 | 
			
		||||
  file(const file&) = delete;
 | 
			
		||||
  void operator=(const file&) = delete;
 | 
			
		||||
 | 
			
		||||
 public:
 | 
			
		||||
  file(file&& other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; }
 | 
			
		||||
 | 
			
		||||
  file& operator=(file&& other) {
 | 
			
		||||
  file& operator=(file&& other) FMT_NOEXCEPT {
 | 
			
		||||
    close();
 | 
			
		||||
    fd_ = other.fd_;
 | 
			
		||||
    other.fd_ = -1;
 | 
			
		||||
@@ -260,6 +270,7 @@ class file {
 | 
			
		||||
 | 
			
		||||
// Returns the memory page size.
 | 
			
		||||
long getpagesize();
 | 
			
		||||
#endif  // FMT_USE_FCNTL
 | 
			
		||||
 | 
			
		||||
#ifdef FMT_LOCALE
 | 
			
		||||
// A "C" numeric locale.
 | 
			
		||||
@@ -283,11 +294,10 @@ class Locale {
 | 
			
		||||
 | 
			
		||||
  locale_t locale_;
 | 
			
		||||
 | 
			
		||||
  Locale(const Locale&) = delete;
 | 
			
		||||
  void operator=(const Locale&) = delete;
 | 
			
		||||
 | 
			
		||||
 public:
 | 
			
		||||
  using type = locale_t;
 | 
			
		||||
  Locale(const Locale&) = delete;
 | 
			
		||||
  void operator=(const Locale&) = delete;
 | 
			
		||||
 | 
			
		||||
  Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", nullptr)) {
 | 
			
		||||
    if (!locale_) FMT_THROW(system_error(errno, "cannot create locale"));
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
// Formatting library for C++
 | 
			
		||||
// Formatting library for C++ - legacy printf implementation
 | 
			
		||||
//
 | 
			
		||||
// Copyright (c) 2012 - 2016, Victor Zverovich
 | 
			
		||||
// All rights reserved.
 | 
			
		||||
@@ -8,7 +8,7 @@
 | 
			
		||||
#ifndef FMT_PRINTF_H_
 | 
			
		||||
#define FMT_PRINTF_H_
 | 
			
		||||
 | 
			
		||||
#include <algorithm>  // std::fill_n
 | 
			
		||||
#include <algorithm>  // std::max
 | 
			
		||||
#include <limits>     // std::numeric_limits
 | 
			
		||||
 | 
			
		||||
#include "ostream.h"
 | 
			
		||||
@@ -16,15 +16,11 @@
 | 
			
		||||
FMT_BEGIN_NAMESPACE
 | 
			
		||||
namespace internal {
 | 
			
		||||
 | 
			
		||||
// A helper function to suppress bogus "conditional expression is constant"
 | 
			
		||||
// warnings.
 | 
			
		||||
template <typename T> inline T const_check(T value) { return value; }
 | 
			
		||||
 | 
			
		||||
// 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 = std::numeric_limits<int>::max();
 | 
			
		||||
    unsigned max = max_value<int>();
 | 
			
		||||
    return value <= max;
 | 
			
		||||
  }
 | 
			
		||||
  static bool fits_in_int(bool) { return true; }
 | 
			
		||||
@@ -33,7 +29,7 @@ template <bool IsSigned> struct int_checker {
 | 
			
		||||
template <> struct int_checker<true> {
 | 
			
		||||
  template <typename T> static bool fits_in_int(T value) {
 | 
			
		||||
    return value >= std::numeric_limits<int>::min() &&
 | 
			
		||||
           value <= std::numeric_limits<int>::max();
 | 
			
		||||
           value <= max_value<int>();
 | 
			
		||||
  }
 | 
			
		||||
  static bool fits_in_int(int) { return true; }
 | 
			
		||||
};
 | 
			
		||||
@@ -158,12 +154,12 @@ template <typename Char> class printf_width_handler {
 | 
			
		||||
 | 
			
		||||
  template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
 | 
			
		||||
  unsigned operator()(T value) {
 | 
			
		||||
    auto width = static_cast<uint32_or_64_t<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 = std::numeric_limits<int>::max();
 | 
			
		||||
    unsigned int_max = max_value<int>();
 | 
			
		||||
    if (width > int_max) FMT_THROW(format_error("number is too big"));
 | 
			
		||||
    return static_cast<unsigned>(width);
 | 
			
		||||
  }
 | 
			
		||||
@@ -235,7 +231,7 @@ class printf_arg_formatter : public internal::arg_formatter_base<Range> {
 | 
			
		||||
  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(std::is_integral<T>::value)>
 | 
			
		||||
  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.
 | 
			
		||||
@@ -332,14 +328,14 @@ template <typename OutputIt, typename Char> class basic_printf_context {
 | 
			
		||||
 | 
			
		||||
  OutputIt out_;
 | 
			
		||||
  basic_format_args<basic_printf_context> args_;
 | 
			
		||||
  basic_parse_context<Char> parse_ctx_;
 | 
			
		||||
  basic_format_parse_context<Char> 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 equal
 | 
			
		||||
  // to the maximum unsigned value, the next argument.
 | 
			
		||||
  format_arg get_arg(unsigned arg_index = std::numeric_limits<unsigned>::max());
 | 
			
		||||
  format_arg get_arg(unsigned arg_index = internal::max_value<unsigned>());
 | 
			
		||||
 | 
			
		||||
  // Parses argument index, flags and width and returns the argument index.
 | 
			
		||||
  unsigned parse_header(const Char*& it, const Char* end, format_specs& specs);
 | 
			
		||||
@@ -361,15 +357,14 @@ template <typename OutputIt, typename Char> class basic_printf_context {
 | 
			
		||||
 | 
			
		||||
  format_arg arg(unsigned id) const { return args_.get(id); }
 | 
			
		||||
 | 
			
		||||
  basic_parse_context<Char>& parse_context() { return parse_ctx_; }
 | 
			
		||||
  basic_format_parse_context<Char>& 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<internal::buffer_range<Char>>>
 | 
			
		||||
  template <typename ArgFormatter = printf_arg_formatter<buffer_range<Char>>>
 | 
			
		||||
  OutputIt format();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -403,7 +398,7 @@ void basic_printf_context<OutputIt, Char>::parse_flags(format_specs& specs,
 | 
			
		||||
template <typename OutputIt, typename Char>
 | 
			
		||||
typename basic_printf_context<OutputIt, Char>::format_arg
 | 
			
		||||
basic_printf_context<OutputIt, Char>::get_arg(unsigned arg_index) {
 | 
			
		||||
  if (arg_index == std::numeric_limits<unsigned>::max())
 | 
			
		||||
  if (arg_index == internal::max_value<unsigned>())
 | 
			
		||||
    arg_index = parse_ctx_.next_arg_id();
 | 
			
		||||
  else
 | 
			
		||||
    parse_ctx_.check_arg_id(--arg_index);
 | 
			
		||||
@@ -413,7 +408,7 @@ basic_printf_context<OutputIt, Char>::get_arg(unsigned arg_index) {
 | 
			
		||||
template <typename OutputIt, typename Char>
 | 
			
		||||
unsigned basic_printf_context<OutputIt, Char>::parse_header(
 | 
			
		||||
    const Char*& it, const Char* end, format_specs& specs) {
 | 
			
		||||
  unsigned arg_index = std::numeric_limits<unsigned>::max();
 | 
			
		||||
  unsigned arg_index = internal::max_value<unsigned>();
 | 
			
		||||
  char_type c = *it;
 | 
			
		||||
  if (c >= '0' && c <= '9') {
 | 
			
		||||
    // Parse an argument index (if followed by '$') or a width possibly
 | 
			
		||||
@@ -470,6 +465,7 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
 | 
			
		||||
 | 
			
		||||
    // Parse argument index, flags and width.
 | 
			
		||||
    unsigned arg_index = parse_header(it, end, specs);
 | 
			
		||||
    if (arg_index == 0) on_error("argument index out of range");
 | 
			
		||||
 | 
			
		||||
    // Parse precision.
 | 
			
		||||
    if (it != end && *it == '.') {
 | 
			
		||||
 
 | 
			
		||||
@@ -246,7 +246,8 @@ 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_convertible<T, std::basic_string<Char>>::value &&
 | 
			
		||||
      !std::is_constructible<internal::std_string_view<Char>, T>::value;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template <typename RangeT, typename Char>
 | 
			
		||||
@@ -283,6 +284,82 @@ struct formatter<RangeT, Char,
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
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};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
FMT_END_NAMESPACE
 | 
			
		||||
 | 
			
		||||
#endif  // FMT_RANGES_H_
 | 
			
		||||
 
 | 
			
		||||
@@ -1,293 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * For conversion between std::chrono::durations without undefined
 | 
			
		||||
 * behaviour or erroneous results.
 | 
			
		||||
 * This is a stripped down version of duration_cast, for inclusion in fmt.
 | 
			
		||||
 * See https://github.com/pauldreik/safe_duration_cast
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright Paul Dreik 2019
 | 
			
		||||
 *
 | 
			
		||||
 * This file is licensed under the fmt license, see format.h
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <chrono>
 | 
			
		||||
#include <cmath>
 | 
			
		||||
#include <limits>
 | 
			
		||||
#include <type_traits>
 | 
			
		||||
 | 
			
		||||
#include "format.h"
 | 
			
		||||
 | 
			
		||||
FMT_BEGIN_NAMESPACE
 | 
			
		||||
 | 
			
		||||
namespace safe_duration_cast {
 | 
			
		||||
 | 
			
		||||
template <typename To, typename From,
 | 
			
		||||
          FMT_ENABLE_IF(!std::is_same<From, To>::value &&
 | 
			
		||||
                        std::numeric_limits<From>::is_signed ==
 | 
			
		||||
                            std::numeric_limits<To>::is_signed)>
 | 
			
		||||
FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
 | 
			
		||||
  ec = 0;
 | 
			
		||||
  using F = std::numeric_limits<From>;
 | 
			
		||||
  using T = std::numeric_limits<To>;
 | 
			
		||||
  static_assert(F::is_integer, "From must be integral");
 | 
			
		||||
  static_assert(T::is_integer, "To must be integral");
 | 
			
		||||
 | 
			
		||||
  // A and B are both signed, or both unsigned.
 | 
			
		||||
  if (F::digits <= T::digits) {
 | 
			
		||||
    // From fits in To without any problem.
 | 
			
		||||
  } else {
 | 
			
		||||
    // From does not always fit in To, resort to a dynamic check.
 | 
			
		||||
    if (from < T::min() || from > T::max()) {
 | 
			
		||||
      // outside range.
 | 
			
		||||
      ec = 1;
 | 
			
		||||
      return {};
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return static_cast<To>(from);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * converts From to To, without loss. If the dynamic value of from
 | 
			
		||||
 * can't be converted to To without loss, ec is set.
 | 
			
		||||
 */
 | 
			
		||||
template <typename To, typename From,
 | 
			
		||||
          FMT_ENABLE_IF(!std::is_same<From, To>::value &&
 | 
			
		||||
                        std::numeric_limits<From>::is_signed !=
 | 
			
		||||
                            std::numeric_limits<To>::is_signed)>
 | 
			
		||||
FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
 | 
			
		||||
  ec = 0;
 | 
			
		||||
  using F = std::numeric_limits<From>;
 | 
			
		||||
  using T = std::numeric_limits<To>;
 | 
			
		||||
  static_assert(F::is_integer, "From must be integral");
 | 
			
		||||
  static_assert(T::is_integer, "To must be integral");
 | 
			
		||||
 | 
			
		||||
  if (F::is_signed && !T::is_signed) {
 | 
			
		||||
    // From may be negative, not allowed!
 | 
			
		||||
    if (from < 0) {
 | 
			
		||||
      ec = 1;
 | 
			
		||||
      return {};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // From is positive. Can it always fit in To?
 | 
			
		||||
    if (F::digits <= T::digits) {
 | 
			
		||||
      // yes, From always fits in To.
 | 
			
		||||
    } else {
 | 
			
		||||
      // from may not fit in To, we have to do a dynamic check
 | 
			
		||||
      if (from > static_cast<From>(T::max())) {
 | 
			
		||||
        ec = 1;
 | 
			
		||||
        return {};
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!F::is_signed && T::is_signed) {
 | 
			
		||||
    // can from be held in To?
 | 
			
		||||
    if (F::digits < T::digits) {
 | 
			
		||||
      // yes, From always fits in To.
 | 
			
		||||
    } else {
 | 
			
		||||
      // from may not fit in To, we have to do a dynamic check
 | 
			
		||||
      if (from > static_cast<From>(T::max())) {
 | 
			
		||||
        // outside range.
 | 
			
		||||
        ec = 1;
 | 
			
		||||
        return {};
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // reaching here means all is ok for lossless conversion.
 | 
			
		||||
  return static_cast<To>(from);
 | 
			
		||||
 | 
			
		||||
}  // function
 | 
			
		||||
 | 
			
		||||
template <typename To, typename From,
 | 
			
		||||
          FMT_ENABLE_IF(std::is_same<From, To>::value)>
 | 
			
		||||
FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
 | 
			
		||||
  ec = 0;
 | 
			
		||||
  return from;
 | 
			
		||||
}  // function
 | 
			
		||||
 | 
			
		||||
// clang-format off
 | 
			
		||||
/**
 | 
			
		||||
 * converts From to To if possible, otherwise ec is set.
 | 
			
		||||
 *
 | 
			
		||||
 * input                            |    output
 | 
			
		||||
 * ---------------------------------|---------------
 | 
			
		||||
 * NaN                              | NaN
 | 
			
		||||
 * Inf                              | Inf
 | 
			
		||||
 * normal, fits in output           | converted (possibly lossy)
 | 
			
		||||
 * normal, does not fit in output   | ec is set
 | 
			
		||||
 * subnormal                        | best effort
 | 
			
		||||
 * -Inf                             | -Inf
 | 
			
		||||
 */
 | 
			
		||||
// clang-format on
 | 
			
		||||
template <typename To, typename From,
 | 
			
		||||
          FMT_ENABLE_IF(!std::is_same<From, To>::value)>
 | 
			
		||||
FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
 | 
			
		||||
  ec = 0;
 | 
			
		||||
  using T = std::numeric_limits<To>;
 | 
			
		||||
  static_assert(std::is_floating_point<From>::value, "From must be floating");
 | 
			
		||||
  static_assert(std::is_floating_point<To>::value, "To must be floating");
 | 
			
		||||
 | 
			
		||||
  // catch the only happy case
 | 
			
		||||
  if (std::isfinite(from)) {
 | 
			
		||||
    if (from >= T::lowest() && from <= T::max()) {
 | 
			
		||||
      return static_cast<To>(from);
 | 
			
		||||
    }
 | 
			
		||||
    // not within range.
 | 
			
		||||
    ec = 1;
 | 
			
		||||
    return {};
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // nan and inf will be preserved
 | 
			
		||||
  return static_cast<To>(from);
 | 
			
		||||
}  // function
 | 
			
		||||
 | 
			
		||||
template <typename To, typename From,
 | 
			
		||||
          FMT_ENABLE_IF(std::is_same<From, To>::value)>
 | 
			
		||||
FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
 | 
			
		||||
  ec = 0;
 | 
			
		||||
  static_assert(std::is_floating_point<From>::value, "From must be floating");
 | 
			
		||||
  return from;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * safe duration cast between integral durations
 | 
			
		||||
 */
 | 
			
		||||
template <typename To, typename FromRep, typename FromPeriod,
 | 
			
		||||
          FMT_ENABLE_IF(std::is_integral<FromRep>::value),
 | 
			
		||||
          FMT_ENABLE_IF(std::is_integral<typename To::rep>::value)>
 | 
			
		||||
To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
 | 
			
		||||
                      int& ec) {
 | 
			
		||||
  using From = std::chrono::duration<FromRep, FromPeriod>;
 | 
			
		||||
  ec = 0;
 | 
			
		||||
  // the basic idea is that we need to convert from count() in the from type
 | 
			
		||||
  // to count() in the To type, by multiplying it with this:
 | 
			
		||||
  using Factor = std::ratio_divide<typename From::period, typename To::period>;
 | 
			
		||||
 | 
			
		||||
  static_assert(Factor::num > 0, "num must be positive");
 | 
			
		||||
  static_assert(Factor::den > 0, "den must be positive");
 | 
			
		||||
 | 
			
		||||
  // the conversion is like this: multiply from.count() with Factor::num
 | 
			
		||||
  // /Factor::den and convert it to To::rep, all this without
 | 
			
		||||
  // overflow/underflow. let's start by finding a suitable type that can hold
 | 
			
		||||
  // both To, From and Factor::num
 | 
			
		||||
  using IntermediateRep =
 | 
			
		||||
      typename std::common_type<typename From::rep, typename To::rep,
 | 
			
		||||
                                decltype(Factor::num)>::type;
 | 
			
		||||
 | 
			
		||||
  // safe conversion to IntermediateRep
 | 
			
		||||
  IntermediateRep count =
 | 
			
		||||
      lossless_integral_conversion<IntermediateRep>(from.count(), ec);
 | 
			
		||||
  if (ec) {
 | 
			
		||||
    return {};
 | 
			
		||||
  }
 | 
			
		||||
  // multiply with Factor::num without overflow or underflow
 | 
			
		||||
  if (Factor::num != 1) {
 | 
			
		||||
    constexpr auto max1 =
 | 
			
		||||
        std::numeric_limits<IntermediateRep>::max() / Factor::num;
 | 
			
		||||
    if (count > max1) {
 | 
			
		||||
      ec = 1;
 | 
			
		||||
      return {};
 | 
			
		||||
    }
 | 
			
		||||
    constexpr auto min1 =
 | 
			
		||||
        std::numeric_limits<IntermediateRep>::min() / Factor::num;
 | 
			
		||||
    if (count < min1) {
 | 
			
		||||
      ec = 1;
 | 
			
		||||
      return {};
 | 
			
		||||
    }
 | 
			
		||||
    count *= Factor::num;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // this can't go wrong, right? den>0 is checked earlier.
 | 
			
		||||
  if (Factor::den != 1) {
 | 
			
		||||
    count /= Factor::den;
 | 
			
		||||
  }
 | 
			
		||||
  // convert to the to type, safely
 | 
			
		||||
  using ToRep = typename To::rep;
 | 
			
		||||
  const ToRep tocount = lossless_integral_conversion<ToRep>(count, ec);
 | 
			
		||||
  if (ec) {
 | 
			
		||||
    return {};
 | 
			
		||||
  }
 | 
			
		||||
  return To{tocount};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * safe duration_cast between floating point durations
 | 
			
		||||
 */
 | 
			
		||||
template <typename To, typename FromRep, typename FromPeriod,
 | 
			
		||||
          FMT_ENABLE_IF(std::is_floating_point<FromRep>::value),
 | 
			
		||||
          FMT_ENABLE_IF(std::is_floating_point<typename To::rep>::value)>
 | 
			
		||||
To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
 | 
			
		||||
                      int& ec) {
 | 
			
		||||
  using From = std::chrono::duration<FromRep, FromPeriod>;
 | 
			
		||||
  ec = 0;
 | 
			
		||||
  if (std::isnan(from.count())) {
 | 
			
		||||
    // nan in, gives nan out. easy.
 | 
			
		||||
    return To{std::numeric_limits<typename To::rep>::quiet_NaN()};
 | 
			
		||||
  }
 | 
			
		||||
  // maybe we should also check if from is denormal, and decide what to do about
 | 
			
		||||
  // it.
 | 
			
		||||
 | 
			
		||||
  // +-inf should be preserved.
 | 
			
		||||
  if (std::isinf(from.count())) {
 | 
			
		||||
    return To{from.count()};
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // the basic idea is that we need to convert from count() in the from type
 | 
			
		||||
  // to count() in the To type, by multiplying it with this:
 | 
			
		||||
  using Factor = std::ratio_divide<typename From::period, typename To::period>;
 | 
			
		||||
 | 
			
		||||
  static_assert(Factor::num > 0, "num must be positive");
 | 
			
		||||
  static_assert(Factor::den > 0, "den must be positive");
 | 
			
		||||
 | 
			
		||||
  // the conversion is like this: multiply from.count() with Factor::num
 | 
			
		||||
  // /Factor::den and convert it to To::rep, all this without
 | 
			
		||||
  // overflow/underflow. let's start by finding a suitable type that can hold
 | 
			
		||||
  // both To, From and Factor::num
 | 
			
		||||
  using IntermediateRep =
 | 
			
		||||
      typename std::common_type<typename From::rep, typename To::rep,
 | 
			
		||||
                                decltype(Factor::num)>::type;
 | 
			
		||||
 | 
			
		||||
  // force conversion of From::rep -> IntermediateRep to be safe,
 | 
			
		||||
  // even if it will never happen be narrowing in this context.
 | 
			
		||||
  IntermediateRep count =
 | 
			
		||||
      safe_float_conversion<IntermediateRep>(from.count(), ec);
 | 
			
		||||
  if (ec) {
 | 
			
		||||
    return {};
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // multiply with Factor::num without overflow or underflow
 | 
			
		||||
  if (Factor::num != 1) {
 | 
			
		||||
    constexpr auto max1 = std::numeric_limits<IntermediateRep>::max() /
 | 
			
		||||
                          static_cast<IntermediateRep>(Factor::num);
 | 
			
		||||
    if (count > max1) {
 | 
			
		||||
      ec = 1;
 | 
			
		||||
      return {};
 | 
			
		||||
    }
 | 
			
		||||
    constexpr auto min1 = std::numeric_limits<IntermediateRep>::lowest() /
 | 
			
		||||
                          static_cast<IntermediateRep>(Factor::num);
 | 
			
		||||
    if (count < min1) {
 | 
			
		||||
      ec = 1;
 | 
			
		||||
      return {};
 | 
			
		||||
    }
 | 
			
		||||
    count *= static_cast<IntermediateRep>(Factor::num);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // this can't go wrong, right? den>0 is checked earlier.
 | 
			
		||||
  if (Factor::den != 1) {
 | 
			
		||||
    using common_t = typename std::common_type<IntermediateRep, intmax_t>::type;
 | 
			
		||||
    count /= static_cast<common_t>(Factor::den);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // convert to the to type, safely
 | 
			
		||||
  using ToRep = typename To::rep;
 | 
			
		||||
 | 
			
		||||
  const ToRep tocount = safe_float_conversion<ToRep>(count, ec);
 | 
			
		||||
  if (ec) {
 | 
			
		||||
    return {};
 | 
			
		||||
  }
 | 
			
		||||
  return To{tocount};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace safe_duration_cast
 | 
			
		||||
 | 
			
		||||
FMT_END_NAMESPACE
 | 
			
		||||
							
								
								
									
										16
									
								
								third_party/spdlog/include/spdlog/fmt/fmt.h
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										16
									
								
								third_party/spdlog/include/spdlog/fmt/fmt.h
									
									
									
									
										vendored
									
									
								
							@@ -10,6 +10,13 @@
 | 
			
		||||
// By default spdlog include its own copy.
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
#if defined(__GNUC__) || defined(__clang__)
 | 
			
		||||
#pragma GCC diagnostic push
 | 
			
		||||
#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
 | 
			
		||||
#pragma GCC diagnostic ignored "-Wsign-conversion"
 | 
			
		||||
#endif // __GNUC__ || __clang__
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#if !defined(SPDLOG_FMT_EXTERNAL)
 | 
			
		||||
#ifdef SPDLOG_HEADER_ONLY
 | 
			
		||||
#ifndef FMT_HEADER_ONLY
 | 
			
		||||
@@ -22,6 +29,11 @@
 | 
			
		||||
#include "bundled/core.h"
 | 
			
		||||
#include "bundled/format.h"
 | 
			
		||||
#else // SPDLOG_FMT_EXTERNAL is defined - use external fmtlib
 | 
			
		||||
#include "fmt/core.h"
 | 
			
		||||
#include "fmt/format.h"
 | 
			
		||||
#include <fmt/core.h>
 | 
			
		||||
#include <fmt/format.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
// pop warnings supressions
 | 
			
		||||
#if defined(__GNUC__) || defined(__clang__)
 | 
			
		||||
#pragma GCC diagnostic pop
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -3,8 +3,8 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "fmt/fmt.h"
 | 
			
		||||
#include "spdlog/details/log_msg.h"
 | 
			
		||||
#include <spdlog/fmt/fmt.h>
 | 
			
		||||
#include <spdlog/details/log_msg.h>
 | 
			
		||||
 | 
			
		||||
namespace spdlog {
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										34
									
								
								third_party/spdlog/include/spdlog/logger-inl.h
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										34
									
								
								third_party/spdlog/include/spdlog/logger-inl.h
									
									
									
									
										vendored
									
									
								
							@@ -4,12 +4,12 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#ifndef SPDLOG_HEADER_ONLY
 | 
			
		||||
#include "spdlog/logger.h"
 | 
			
		||||
#include <spdlog/logger.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include "spdlog/sinks/sink.h"
 | 
			
		||||
#include "spdlog/details/backtracer.h"
 | 
			
		||||
#include "spdlog/details/pattern_formatter.h"
 | 
			
		||||
#include <spdlog/sinks/sink.h>
 | 
			
		||||
#include <spdlog/details/backtracer.h>
 | 
			
		||||
#include <spdlog/details/pattern_formatter.h>
 | 
			
		||||
 | 
			
		||||
#include <cstdio>
 | 
			
		||||
 | 
			
		||||
@@ -64,11 +64,6 @@ SPDLOG_INLINE void swap(logger &a, logger &b)
 | 
			
		||||
    a.swap(b);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
SPDLOG_INLINE bool logger::should_log(level::level_enum msg_level) const
 | 
			
		||||
{
 | 
			
		||||
    return msg_level >= level_.load(std::memory_order_relaxed);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
SPDLOG_INLINE void logger::set_level(level::level_enum log_level)
 | 
			
		||||
{
 | 
			
		||||
    level_.store(log_level);
 | 
			
		||||
@@ -85,7 +80,7 @@ SPDLOG_INLINE const std::string &logger::name() const
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// set formatting for the sinks in this logger.
 | 
			
		||||
// each sink will get a seperate instance of the formatter object.
 | 
			
		||||
// 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)
 | 
			
		||||
@@ -155,7 +150,7 @@ SPDLOG_INLINE std::vector<sink_ptr> &logger::sinks()
 | 
			
		||||
// error handler
 | 
			
		||||
SPDLOG_INLINE void logger::set_error_handler(err_handler handler)
 | 
			
		||||
{
 | 
			
		||||
    custom_err_handler_ = handler;
 | 
			
		||||
    custom_err_handler_ = std::move(handler);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// create new logger with same sinks and configuration.
 | 
			
		||||
@@ -167,6 +162,18 @@ SPDLOG_INLINE std::shared_ptr<logger> logger::clone(std::string logger_name)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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_)
 | 
			
		||||
@@ -202,10 +209,10 @@ SPDLOG_INLINE void logger::flush_()
 | 
			
		||||
SPDLOG_INLINE void logger::dump_backtrace_()
 | 
			
		||||
{
 | 
			
		||||
    using details::log_msg;
 | 
			
		||||
    if (tracer_)
 | 
			
		||||
    if (tracer_.enabled())
 | 
			
		||||
    {
 | 
			
		||||
        sink_it_(log_msg{name(), level::info, "****************** Backtrace Start ******************"});
 | 
			
		||||
        tracer_.foreach_pop([this](const details::log_msg &msg) { this->sink_it_(msg); });
 | 
			
		||||
        tracer_.foreach_pop([this](const log_msg &msg) { this->sink_it_(msg); });
 | 
			
		||||
        sink_it_(log_msg{name(), level::info, "****************** Backtrace End ********************"});
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -218,7 +225,6 @@ SPDLOG_INLINE bool logger::should_flush_(const details::log_msg &msg)
 | 
			
		||||
 | 
			
		||||
SPDLOG_INLINE void logger::err_handler_(const std::string &msg)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    if (custom_err_handler_)
 | 
			
		||||
    {
 | 
			
		||||
        custom_err_handler_(msg);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										87
									
								
								third_party/spdlog/include/spdlog/logger.h
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										87
									
								
								third_party/spdlog/include/spdlog/logger.h
									
									
									
									
										vendored
									
									
								
							@@ -3,8 +3,7 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
// Thread safe logger (except for set_pattern(..), set_formatter(..) and
 | 
			
		||||
// set_error_handler())
 | 
			
		||||
// 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:
 | 
			
		||||
@@ -15,12 +14,12 @@
 | 
			
		||||
// 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"
 | 
			
		||||
#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"
 | 
			
		||||
#include <spdlog/details/os.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <vector>
 | 
			
		||||
@@ -77,8 +76,9 @@ public:
 | 
			
		||||
    template<typename... Args>
 | 
			
		||||
    void log(source_loc loc, level::level_enum lvl, string_view_t fmt, const Args &... args)
 | 
			
		||||
    {
 | 
			
		||||
        auto level_enabled = should_log(lvl);
 | 
			
		||||
        if (!level_enabled && !tracer_)
 | 
			
		||||
        bool log_enabled = should_log(lvl);
 | 
			
		||||
        bool traceback_enabled = tracer_.enabled();
 | 
			
		||||
        if (!log_enabled && !traceback_enabled)
 | 
			
		||||
        {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
@@ -87,14 +87,7 @@ public:
 | 
			
		||||
            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()));
 | 
			
		||||
            if (level_enabled)
 | 
			
		||||
            {
 | 
			
		||||
                sink_it_(log_msg);
 | 
			
		||||
            }
 | 
			
		||||
            if (tracer_)
 | 
			
		||||
            {
 | 
			
		||||
                tracer_.push_back(log_msg);
 | 
			
		||||
            }
 | 
			
		||||
            log_it_(log_msg, log_enabled, traceback_enabled);
 | 
			
		||||
        }
 | 
			
		||||
        SPDLOG_LOGGER_CATCH()
 | 
			
		||||
    }
 | 
			
		||||
@@ -151,24 +144,15 @@ public:
 | 
			
		||||
    template<class T, typename std::enable_if<std::is_convertible<const T &, spdlog::string_view_t>::value, T>::type * = nullptr>
 | 
			
		||||
    void log(source_loc loc, level::level_enum lvl, const T &msg)
 | 
			
		||||
    {
 | 
			
		||||
        auto level_enabled = should_log(lvl);
 | 
			
		||||
        if (!level_enabled && !tracer_)
 | 
			
		||||
        bool log_enabled = should_log(lvl);
 | 
			
		||||
        bool traceback_enabled = tracer_.enabled();
 | 
			
		||||
        if (!log_enabled && !traceback_enabled)
 | 
			
		||||
        {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        SPDLOG_TRY
 | 
			
		||||
        {
 | 
			
		||||
            details::log_msg log_msg(loc, name_, lvl, msg);
 | 
			
		||||
            if (level_enabled)
 | 
			
		||||
            {
 | 
			
		||||
                sink_it_(log_msg);
 | 
			
		||||
            }
 | 
			
		||||
            if (tracer_)
 | 
			
		||||
            {
 | 
			
		||||
                tracer_.push_back(log_msg);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        SPDLOG_LOGGER_CATCH()
 | 
			
		||||
 | 
			
		||||
        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)
 | 
			
		||||
@@ -229,8 +213,9 @@ public:
 | 
			
		||||
    template<typename... Args>
 | 
			
		||||
    void log(source_loc loc, level::level_enum lvl, wstring_view_t fmt, const Args &... args)
 | 
			
		||||
    {
 | 
			
		||||
        auto level_enabled = should_log(lvl);
 | 
			
		||||
        if (!level_enabled && !tracer_)
 | 
			
		||||
        bool log_enabled = should_log(lvl);
 | 
			
		||||
        bool traceback_enabled = tracer_.enabled();
 | 
			
		||||
        if (!log_enabled && !traceback_enabled)
 | 
			
		||||
        {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
@@ -243,15 +228,7 @@ public:
 | 
			
		||||
            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()));
 | 
			
		||||
 | 
			
		||||
            if (level_enabled)
 | 
			
		||||
            {
 | 
			
		||||
                sink_it_(log_msg);
 | 
			
		||||
            }
 | 
			
		||||
            if (tracer_)
 | 
			
		||||
            {
 | 
			
		||||
                tracer_.push_back(log_msg);
 | 
			
		||||
            }
 | 
			
		||||
            log_it_(log_msg, log_enabled, traceback_enabled);
 | 
			
		||||
        }
 | 
			
		||||
        SPDLOG_LOGGER_CATCH()
 | 
			
		||||
    }
 | 
			
		||||
@@ -302,25 +279,36 @@ public:
 | 
			
		||||
    template<class T, typename std::enable_if<is_convertible_to_wstring_view<const T &>::value, T>::type * = nullptr>
 | 
			
		||||
    void log(source_loc loc, level::level_enum lvl, const T &msg)
 | 
			
		||||
    {
 | 
			
		||||
        if (!should_log(lvl))
 | 
			
		||||
        bool log_enabled = should_log(lvl);
 | 
			
		||||
        bool traceback_enabled = tracer_.enabled();
 | 
			
		||||
        if (!log_enabled && !traceback_enabled)
 | 
			
		||||
        {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        try
 | 
			
		||||
        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()));
 | 
			
		||||
            sink_it_(log_msg);
 | 
			
		||||
            log_it_(log_msg, log_enabled, traceback_enabled);
 | 
			
		||||
        }
 | 
			
		||||
        SPDLOG_LOGGER_CATCH()
 | 
			
		||||
    }
 | 
			
		||||
#endif // _WIN32
 | 
			
		||||
#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT
 | 
			
		||||
 | 
			
		||||
    bool should_log(level::level_enum msg_level) const;
 | 
			
		||||
    // 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);
 | 
			
		||||
 | 
			
		||||
@@ -329,7 +317,7 @@ public:
 | 
			
		||||
    const std::string &name() const;
 | 
			
		||||
 | 
			
		||||
    // set formatting for the sinks in this logger.
 | 
			
		||||
    // each sink will get a seperate instance of the formatter object.
 | 
			
		||||
    // 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);
 | 
			
		||||
@@ -364,6 +352,9 @@ protected:
 | 
			
		||||
    err_handler custom_err_handler_{nullptr};
 | 
			
		||||
    details::backtracer tracer_;
 | 
			
		||||
 | 
			
		||||
    // 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_();
 | 
			
		||||
 
 | 
			
		||||
@@ -5,11 +5,11 @@
 | 
			
		||||
 | 
			
		||||
#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 <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>
 | 
			
		||||
 
 | 
			
		||||
@@ -4,11 +4,11 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#ifndef SPDLOG_HEADER_ONLY
 | 
			
		||||
#include "spdlog/sinks/ansicolor_sink.h"
 | 
			
		||||
#include <spdlog/sinks/ansicolor_sink.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include "spdlog/details/pattern_formatter.h"
 | 
			
		||||
#include "spdlog/details/os.h"
 | 
			
		||||
#include <spdlog/details/pattern_formatter.h>
 | 
			
		||||
#include <spdlog/details/os.h>
 | 
			
		||||
 | 
			
		||||
namespace spdlog {
 | 
			
		||||
namespace sinks {
 | 
			
		||||
 
 | 
			
		||||
@@ -3,9 +3,9 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "spdlog/details/console_globals.h"
 | 
			
		||||
#include "spdlog/details/null_mutex.h"
 | 
			
		||||
#include "spdlog/sinks/sink.h"
 | 
			
		||||
#include <spdlog/details/console_globals.h>
 | 
			
		||||
#include <spdlog/details/null_mutex.h>
 | 
			
		||||
#include <spdlog/sinks/sink.h>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <mutex>
 | 
			
		||||
#include <string>
 | 
			
		||||
 
 | 
			
		||||
@@ -4,11 +4,11 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#ifndef SPDLOG_HEADER_ONLY
 | 
			
		||||
#include "spdlog/sinks/base_sink.h"
 | 
			
		||||
#include <spdlog/sinks/base_sink.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include "spdlog/common.h"
 | 
			
		||||
#include "spdlog/details/pattern_formatter.h"
 | 
			
		||||
#include <spdlog/common.h>
 | 
			
		||||
#include <spdlog/details/pattern_formatter.h>
 | 
			
		||||
 | 
			
		||||
#include <memory>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -9,9 +9,9 @@
 | 
			
		||||
// implementers..
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
#include "spdlog/common.h"
 | 
			
		||||
#include "spdlog/details/log_msg.h"
 | 
			
		||||
#include "spdlog/sinks/sink.h"
 | 
			
		||||
#include <spdlog/common.h>
 | 
			
		||||
#include <spdlog/details/log_msg.h>
 | 
			
		||||
#include <spdlog/sinks/sink.h>
 | 
			
		||||
 | 
			
		||||
namespace spdlog {
 | 
			
		||||
namespace sinks {
 | 
			
		||||
 
 | 
			
		||||
@@ -4,11 +4,11 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#ifndef SPDLOG_HEADER_ONLY
 | 
			
		||||
#include "spdlog/sinks/basic_file_sink.h"
 | 
			
		||||
#include <spdlog/sinks/basic_file_sink.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include "spdlog/common.h"
 | 
			
		||||
#include "spdlog/details/os.h"
 | 
			
		||||
#include <spdlog/common.h>
 | 
			
		||||
#include <spdlog/details/os.h>
 | 
			
		||||
 | 
			
		||||
namespace spdlog {
 | 
			
		||||
namespace sinks {
 | 
			
		||||
 
 | 
			
		||||
@@ -3,10 +3,10 @@
 | 
			
		||||
 | 
			
		||||
#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 <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>
 | 
			
		||||
 
 | 
			
		||||
@@ -3,13 +3,14 @@
 | 
			
		||||
 | 
			
		||||
#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/synchronous_factory.h"
 | 
			
		||||
#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>
 | 
			
		||||
@@ -78,12 +79,7 @@ public:
 | 
			
		||||
protected:
 | 
			
		||||
    void sink_it_(const details::log_msg &msg) override
 | 
			
		||||
    {
 | 
			
		||||
#ifdef SPDLOG_NO_DATETIME
 | 
			
		||||
        auto time = log_clock::now();
 | 
			
		||||
#else
 | 
			
		||||
        auto time = msg.time;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
        bool should_rotate = time >= rotation_tp_;
 | 
			
		||||
        if (should_rotate)
 | 
			
		||||
        {
 | 
			
		||||
@@ -95,7 +91,7 @@ protected:
 | 
			
		||||
        base_sink<Mutex>::formatter_->format(msg, formatted);
 | 
			
		||||
        file_helper_.write(formatted);
 | 
			
		||||
 | 
			
		||||
        // Do the cleaning ony at the end because it might throw on failure.
 | 
			
		||||
        // Do the cleaning only at the end because it might throw on failure.
 | 
			
		||||
        if (should_rotate && max_files_ > 0)
 | 
			
		||||
        {
 | 
			
		||||
            delete_old_();
 | 
			
		||||
 
 | 
			
		||||
@@ -4,9 +4,9 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "base_sink.h"
 | 
			
		||||
#include "spdlog/details/log_msg.h"
 | 
			
		||||
#include "spdlog/details/null_mutex.h"
 | 
			
		||||
#include "spdlog/details/pattern_formatter.h"
 | 
			
		||||
#include <spdlog/details/log_msg.h>
 | 
			
		||||
#include <spdlog/details/null_mutex.h>
 | 
			
		||||
#include <spdlog/details/pattern_formatter.h>
 | 
			
		||||
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <memory>
 | 
			
		||||
@@ -24,6 +24,10 @@ 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;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -4,8 +4,8 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "dist_sink.h"
 | 
			
		||||
#include "spdlog/details/null_mutex.h"
 | 
			
		||||
#include "spdlog/details/log_msg.h"
 | 
			
		||||
#include <spdlog/details/null_mutex.h>
 | 
			
		||||
#include <spdlog/details/log_msg.h>
 | 
			
		||||
 | 
			
		||||
#include <mutex>
 | 
			
		||||
#include <string>
 | 
			
		||||
@@ -16,7 +16,7 @@
 | 
			
		||||
//
 | 
			
		||||
// Example:
 | 
			
		||||
//
 | 
			
		||||
//     #include "spdlog/sinks/dup_filter_sink.h"
 | 
			
		||||
//     #include <spdlog/sinks/dup_filter_sink.h>
 | 
			
		||||
//
 | 
			
		||||
//     int main() {
 | 
			
		||||
//         auto dup_filter = std::make_shared<dup_filter_sink_st>(std::chrono::seconds(5));
 | 
			
		||||
@@ -33,10 +33,6 @@
 | 
			
		||||
//       [2019-06-25 17:50:56.512] [logger] [info] Skipped 3 duplicate messages..
 | 
			
		||||
//       [2019-06-25 17:50:56.512] [logger] [info] Different Hello
 | 
			
		||||
 | 
			
		||||
#ifdef SPDLOG_NO_DATETIME
 | 
			
		||||
#error "spdlog::sinks::dup_filter_sink: cannot work when SPDLOG_NO_DATETIME is defined"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
namespace spdlog {
 | 
			
		||||
namespace sinks {
 | 
			
		||||
template<typename Mutex>
 | 
			
		||||
 
 | 
			
		||||
@@ -5,8 +5,8 @@
 | 
			
		||||
 | 
			
		||||
#if defined(_WIN32)
 | 
			
		||||
 | 
			
		||||
#include "spdlog/details/null_mutex.h"
 | 
			
		||||
#include "spdlog/sinks/base_sink.h"
 | 
			
		||||
#include <spdlog/details/null_mutex.h>
 | 
			
		||||
#include <spdlog/sinks/base_sink.h>
 | 
			
		||||
 | 
			
		||||
#include <winbase.h>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -3,9 +3,9 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "spdlog/details/null_mutex.h"
 | 
			
		||||
#include "spdlog/sinks/base_sink.h"
 | 
			
		||||
#include "spdlog/details/synchronous_factory.h"
 | 
			
		||||
#include <spdlog/details/null_mutex.h>
 | 
			
		||||
#include <spdlog/sinks/base_sink.h>
 | 
			
		||||
#include <spdlog/details/synchronous_factory.h>
 | 
			
		||||
 | 
			
		||||
#include <mutex>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -3,8 +3,8 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "spdlog/details/null_mutex.h"
 | 
			
		||||
#include "spdlog/sinks/base_sink.h"
 | 
			
		||||
#include <spdlog/details/null_mutex.h>
 | 
			
		||||
#include <spdlog/sinks/base_sink.h>
 | 
			
		||||
 | 
			
		||||
#include <mutex>
 | 
			
		||||
#include <ostream>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										72
									
								
								third_party/spdlog/include/spdlog/sinks/ringbuffer_sink.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								third_party/spdlog/include/spdlog/sinks/ringbuffer_sink.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,72 @@
 | 
			
		||||
// 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 n_items = lim > 0 ? (std::min)(lim, q_.size()) : q_.size();
 | 
			
		||||
        std::vector<details::log_msg_buffer> ret;
 | 
			
		||||
        ret.reserve(n_items);
 | 
			
		||||
        for (size_t i = 0; i < n_items; 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 n_items = lim > 0 ? (std::min)(lim, q_.size()) : q_.size();
 | 
			
		||||
        std::vector<std::string> ret;
 | 
			
		||||
        ret.reserve(n_items);
 | 
			
		||||
        for (size_t i = 0; i < n_items; 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
 | 
			
		||||
@@ -4,14 +4,14 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#ifndef SPDLOG_HEADER_ONLY
 | 
			
		||||
#include "spdlog/sinks/rotating_file_sink.h"
 | 
			
		||||
#include <spdlog/sinks/rotating_file_sink.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include "spdlog/common.h"
 | 
			
		||||
#include <spdlog/common.h>
 | 
			
		||||
 | 
			
		||||
#include "spdlog/details/file_helper.h"
 | 
			
		||||
#include "spdlog/details/null_mutex.h"
 | 
			
		||||
#include "spdlog/fmt/fmt.h"
 | 
			
		||||
#include <spdlog/details/file_helper.h>
 | 
			
		||||
#include <spdlog/details/null_mutex.h>
 | 
			
		||||
#include <spdlog/fmt/fmt.h>
 | 
			
		||||
 | 
			
		||||
#include <cerrno>
 | 
			
		||||
#include <chrono>
 | 
			
		||||
@@ -88,11 +88,12 @@ 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 (!details::file_helper::file_exists(src))
 | 
			
		||||
        if (!path_exists(src))
 | 
			
		||||
        {
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -3,10 +3,10 @@
 | 
			
		||||
 | 
			
		||||
#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 <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>
 | 
			
		||||
 
 | 
			
		||||
@@ -4,10 +4,10 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#ifndef SPDLOG_HEADER_ONLY
 | 
			
		||||
#include "spdlog/sinks/sink.h"
 | 
			
		||||
#include <spdlog/sinks/sink.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include "spdlog/common.h"
 | 
			
		||||
#include <spdlog/common.h>
 | 
			
		||||
 | 
			
		||||
SPDLOG_INLINE bool spdlog::sinks::sink::should_log(spdlog::level::level_enum msg_level) const
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -3,8 +3,8 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "spdlog/details/log_msg.h"
 | 
			
		||||
#include "spdlog/formatter.h"
 | 
			
		||||
#include <spdlog/details/log_msg.h>
 | 
			
		||||
#include <spdlog/formatter.h>
 | 
			
		||||
 | 
			
		||||
namespace spdlog {
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -4,11 +4,11 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#ifndef SPDLOG_HEADER_ONLY
 | 
			
		||||
#include "spdlog/sinks/stdout_color_sinks.h"
 | 
			
		||||
#include <spdlog/sinks/stdout_color_sinks.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include "spdlog/logger.h"
 | 
			
		||||
#include "spdlog/common.h"
 | 
			
		||||
#include <spdlog/logger.h>
 | 
			
		||||
#include <spdlog/common.h>
 | 
			
		||||
 | 
			
		||||
namespace spdlog {
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -4,12 +4,12 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
#include "spdlog/sinks/wincolor_sink.h"
 | 
			
		||||
#include <spdlog/sinks/wincolor_sink.h>
 | 
			
		||||
#else
 | 
			
		||||
#include "spdlog/sinks/ansicolor_sink.h"
 | 
			
		||||
#include <spdlog/sinks/ansicolor_sink.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include "spdlog/details/synchronous_factory.h"
 | 
			
		||||
#include <spdlog/details/synchronous_factory.h>
 | 
			
		||||
 | 
			
		||||
namespace spdlog {
 | 
			
		||||
namespace sinks {
 | 
			
		||||
 
 | 
			
		||||
@@ -4,11 +4,11 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#ifndef SPDLOG_HEADER_ONLY
 | 
			
		||||
#include "spdlog/sinks/stdout_sinks.h"
 | 
			
		||||
#include <spdlog/sinks/stdout_sinks.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include "spdlog/details/console_globals.h"
 | 
			
		||||
#include "spdlog/details/pattern_formatter.h"
 | 
			
		||||
#include <spdlog/details/console_globals.h>
 | 
			
		||||
#include <spdlog/details/pattern_formatter.h>
 | 
			
		||||
#include <memory>
 | 
			
		||||
 | 
			
		||||
namespace spdlog {
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user