Compare commits
34 Commits
Author | SHA1 | Date | |
---|---|---|---|
4c4f99606e | |||
a61586c846 | |||
d64d50c978 | |||
a64b7b0c4a | |||
0caeb81327 | |||
edac7a0171 | |||
abfadad2e9 | |||
2dc1547bbd | |||
5eb23c9764 | |||
9f4b2856b0 | |||
b5fc10326e | |||
8d3a47a873 | |||
4df58f3059 | |||
06b8cb8d3b | |||
ff81f5b496 | |||
c89f73006e | |||
c28951f049 | |||
dfaaaca223 | |||
c7f0bf3d64 | |||
234ce4c173 | |||
f60293b2e7 | |||
9441095637 | |||
f82d38f758 | |||
a7f42f35db | |||
cb1d1bfd85 | |||
28c3f2ea26 | |||
8dc132dbd3 | |||
98e2fbca6a | |||
fa7f0fadde | |||
2732dfd0f1 | |||
2e4c4b72b6 | |||
fc21ad519b | |||
c65cfd3d26 | |||
8955462f73 |
@ -37,6 +37,7 @@ set( IXWEBSOCKET_SOURCES
|
|||||||
ixwebsocket/IXWebSocketHttpHeaders.cpp
|
ixwebsocket/IXWebSocketHttpHeaders.cpp
|
||||||
ixwebsocket/IXHttpClient.cpp
|
ixwebsocket/IXHttpClient.cpp
|
||||||
ixwebsocket/IXUrlParser.cpp
|
ixwebsocket/IXUrlParser.cpp
|
||||||
|
ixwebsocket/LUrlParser.cpp
|
||||||
ixwebsocket/IXSelectInterrupt.cpp
|
ixwebsocket/IXSelectInterrupt.cpp
|
||||||
ixwebsocket/IXSelectInterruptFactory.cpp
|
ixwebsocket/IXSelectInterruptFactory.cpp
|
||||||
ixwebsocket/IXConnectionState.cpp
|
ixwebsocket/IXConnectionState.cpp
|
||||||
@ -65,6 +66,7 @@ set( IXWEBSOCKET_HEADERS
|
|||||||
ixwebsocket/libwshandshake.hpp
|
ixwebsocket/libwshandshake.hpp
|
||||||
ixwebsocket/IXHttpClient.h
|
ixwebsocket/IXHttpClient.h
|
||||||
ixwebsocket/IXUrlParser.h
|
ixwebsocket/IXUrlParser.h
|
||||||
|
ixwebsocket/LUrlParser.h
|
||||||
ixwebsocket/IXSelectInterrupt.h
|
ixwebsocket/IXSelectInterrupt.h
|
||||||
ixwebsocket/IXSelectInterruptFactory.h
|
ixwebsocket/IXSelectInterruptFactory.h
|
||||||
ixwebsocket/IXConnectionState.h
|
ixwebsocket/IXConnectionState.h
|
||||||
|
@ -1 +1 @@
|
|||||||
1.4.3
|
1.5.3
|
||||||
|
28
README.md
28
README.md
@ -12,8 +12,6 @@
|
|||||||
* Android
|
* Android
|
||||||
* Windows (no TLS)
|
* Windows (no TLS)
|
||||||
|
|
||||||
The code was made to compile once on Windows but support is currently broken on this platform.
|
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
The [*ws*](https://github.com/machinezone/IXWebSocket/tree/master/ws) folder countains many interactive programs for chat, [file transfers](https://github.com/machinezone/IXWebSocket/blob/master/ws/ws_send.cpp), [curl like](https://github.com/machinezone/IXWebSocket/blob/master/ws/ws_http_client.cpp) http clients, demonstrating client and server usage.
|
The [*ws*](https://github.com/machinezone/IXWebSocket/tree/master/ws) folder countains many interactive programs for chat, [file transfers](https://github.com/machinezone/IXWebSocket/blob/master/ws/ws_send.cpp), [curl like](https://github.com/machinezone/IXWebSocket/blob/master/ws/ws_http_client.cpp) http clients, demonstrating client and server usage.
|
||||||
@ -39,7 +37,7 @@ webSocket.setOnMessageCallback(
|
|||||||
const ix::WebSocketOpenInfo& openInfo,
|
const ix::WebSocketOpenInfo& openInfo,
|
||||||
const ix::WebSocketCloseInfo& closeInfo)
|
const ix::WebSocketCloseInfo& closeInfo)
|
||||||
{
|
{
|
||||||
if (messageType == ix::WebSocket_MessageType_Message)
|
if (messageType == ix::WebSocketMessageType::Message)
|
||||||
{
|
{
|
||||||
std::cout << str << std::endl;
|
std::cout << str << std::endl;
|
||||||
}
|
}
|
||||||
@ -79,7 +77,7 @@ server.setOnConnectionCallback(
|
|||||||
const ix::WebSocketOpenInfo& openInfo,
|
const ix::WebSocketOpenInfo& openInfo,
|
||||||
const ix::WebSocketCloseInfo& closeInfo)
|
const ix::WebSocketCloseInfo& closeInfo)
|
||||||
{
|
{
|
||||||
if (messageType == ix::WebSocket_MessageType_Open)
|
if (messageType == ix::WebSocketMessageType::Open)
|
||||||
{
|
{
|
||||||
std::cerr << "New connection" << std::endl;
|
std::cerr << "New connection" << std::endl;
|
||||||
|
|
||||||
@ -98,7 +96,7 @@ server.setOnConnectionCallback(
|
|||||||
std::cerr << it.first << ": " << it.second << std::endl;
|
std::cerr << it.first << ": " << it.second << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (messageType == ix::WebSocket_MessageType_Message)
|
else if (messageType == ix::WebSocketMessageType::Message)
|
||||||
{
|
{
|
||||||
// For an echo server, we just send back to the client whatever was received by the server
|
// For an echo server, we just send back to the client whatever was received by the server
|
||||||
// All connected clients are available in an std::set. See the broadcast cpp example.
|
// All connected clients are available in an std::set. See the broadcast cpp example.
|
||||||
@ -303,10 +301,10 @@ If the connection was closed and sending failed, the return value will be set to
|
|||||||
|
|
||||||
`getReadyState()` returns the state of the connection. There are 4 possible states.
|
`getReadyState()` returns the state of the connection. There are 4 possible states.
|
||||||
|
|
||||||
1. WebSocket_ReadyState_Connecting - The connection is not yet open.
|
1. ReadyState::Connecting - The connection is not yet open.
|
||||||
2. WebSocket_ReadyState_Open - The connection is open and ready to communicate.
|
2. ReadyState::Open - The connection is open and ready to communicate.
|
||||||
3. WebSocket_ReadyState_Closing - The connection is in the process of closing.
|
3. ReadyState::Closing - The connection is in the process of closing.
|
||||||
4. WebSocket_MessageType_Close - The connection is closed or could not be opened.
|
4. ReadyState::Closed - The connection is closed or could not be opened.
|
||||||
|
|
||||||
### Open and Close notifications
|
### Open and Close notifications
|
||||||
|
|
||||||
@ -321,7 +319,7 @@ webSocket.setOnMessageCallback(
|
|||||||
const ix::WebSocketOpenInfo& openInfo,
|
const ix::WebSocketOpenInfo& openInfo,
|
||||||
const ix::WebSocketCloseInfo& closeInfo)
|
const ix::WebSocketCloseInfo& closeInfo)
|
||||||
{
|
{
|
||||||
if (messageType == ix::WebSocket_MessageType_Open)
|
if (messageType == ix::WebSocketMessageType::Open)
|
||||||
{
|
{
|
||||||
std::cout << "send greetings" << std::endl;
|
std::cout << "send greetings" << std::endl;
|
||||||
|
|
||||||
@ -332,7 +330,7 @@ webSocket.setOnMessageCallback(
|
|||||||
std::cout << it.first << ": " << it.second << std::endl;
|
std::cout << it.first << ": " << it.second << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (messageType == ix::WebSocket_MessageType_Close)
|
else if (messageType == ix::WebSocketMessageType::Close)
|
||||||
{
|
{
|
||||||
std::cout << "disconnected" << std::endl;
|
std::cout << "disconnected" << std::endl;
|
||||||
|
|
||||||
@ -347,7 +345,7 @@ webSocket.setOnMessageCallback(
|
|||||||
|
|
||||||
### Error notification
|
### Error notification
|
||||||
|
|
||||||
A message will be fired when there is an error with the connection. The message type will be `ix::WebSocket_MessageType_Error`. Multiple fields will be available on the event to describe the error.
|
A message will be fired when there is an error with the connection. The message type will be `ix::WebSocketMessageType::Error`. Multiple fields will be available on the event to describe the error.
|
||||||
|
|
||||||
```
|
```
|
||||||
webSocket.setOnMessageCallback(
|
webSocket.setOnMessageCallback(
|
||||||
@ -358,7 +356,7 @@ webSocket.setOnMessageCallback(
|
|||||||
const ix::WebSocketOpenInfo& openInfo,
|
const ix::WebSocketOpenInfo& openInfo,
|
||||||
const ix::WebSocketCloseInfo& closeInfo)
|
const ix::WebSocketCloseInfo& closeInfo)
|
||||||
{
|
{
|
||||||
if (messageType == ix::WebSocket_MessageType_Error)
|
if (messageType == ix::WebSocketMessageType::Error)
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << "Error: " << error.reason << std::endl;
|
ss << "Error: " << error.reason << std::endl;
|
||||||
@ -398,8 +396,8 @@ webSocket.setOnMessageCallback(
|
|||||||
const ix::WebSocketOpenInfo& openInfo,
|
const ix::WebSocketOpenInfo& openInfo,
|
||||||
const ix::WebSocketCloseInfo& closeInfo)
|
const ix::WebSocketCloseInfo& closeInfo)
|
||||||
{
|
{
|
||||||
if (messageType == ix::WebSocket_MessageType_Ping ||
|
if (messageType == ix::WebSocketMessageType::Ping ||
|
||||||
messageType == ix::WebSocket_MessageType_Pong)
|
messageType == ix::WebSocketMessageType::Pong)
|
||||||
{
|
{
|
||||||
std::cout << "pong data: " << str << std::endl;
|
std::cout << "pong data: " << str << std::endl;
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,14 @@
|
|||||||
image:
|
image:
|
||||||
- Visual Studio 2017
|
- Visual Studio 2017
|
||||||
- Ubuntu
|
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- ls -al
|
- ls -al
|
||||||
- cmd: call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat"
|
- cmd: call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat"
|
||||||
- python test/run.py
|
- cd test
|
||||||
|
- mkdir build
|
||||||
|
- cd build
|
||||||
|
- cmake -G"NMake Makefiles" ..
|
||||||
|
- nmake
|
||||||
|
- ixwebsocket_unittest.exe
|
||||||
|
|
||||||
build: off
|
build: off
|
||||||
|
@ -47,13 +47,12 @@ namespace ix
|
|||||||
|
|
||||||
std::string protocol, host, path, query;
|
std::string protocol, host, path, query;
|
||||||
int port;
|
int port;
|
||||||
bool websocket = false;
|
|
||||||
|
|
||||||
if (!UrlParser::parse(url, protocol, host, path, query, port, websocket))
|
if (!UrlParser::parse(url, protocol, host, path, query, port))
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << "Cannot parse url: " << url;
|
ss << "Cannot parse url: " << url;
|
||||||
return std::make_tuple(code, HttpErrorCode_UrlMalformed,
|
return std::make_tuple(code, HttpErrorCode::UrlMalformed,
|
||||||
headers, payload, ss.str(),
|
headers, payload, ss.str(),
|
||||||
uploadSize, downloadSize);
|
uploadSize, downloadSize);
|
||||||
}
|
}
|
||||||
@ -64,7 +63,7 @@ namespace ix
|
|||||||
|
|
||||||
if (!_socket)
|
if (!_socket)
|
||||||
{
|
{
|
||||||
return std::make_tuple(code, HttpErrorCode_CannotCreateSocket,
|
return std::make_tuple(code, HttpErrorCode::CannotCreateSocket,
|
||||||
headers, payload, errorMsg,
|
headers, payload, errorMsg,
|
||||||
uploadSize, downloadSize);
|
uploadSize, downloadSize);
|
||||||
}
|
}
|
||||||
@ -117,7 +116,7 @@ namespace ix
|
|||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << "Cannot connect to url: " << url;
|
ss << "Cannot connect to url: " << url;
|
||||||
return std::make_tuple(code, HttpErrorCode_CannotConnect,
|
return std::make_tuple(code, HttpErrorCode::CannotConnect,
|
||||||
headers, payload, ss.str(),
|
headers, payload, ss.str(),
|
||||||
uploadSize, downloadSize);
|
uploadSize, downloadSize);
|
||||||
}
|
}
|
||||||
@ -143,7 +142,7 @@ namespace ix
|
|||||||
if (!_socket->writeBytes(req, isCancellationRequested))
|
if (!_socket->writeBytes(req, isCancellationRequested))
|
||||||
{
|
{
|
||||||
std::string errorMsg("Cannot send request");
|
std::string errorMsg("Cannot send request");
|
||||||
return std::make_tuple(code, HttpErrorCode_SendError,
|
return std::make_tuple(code, HttpErrorCode::SendError,
|
||||||
headers, payload, errorMsg,
|
headers, payload, errorMsg,
|
||||||
uploadSize, downloadSize);
|
uploadSize, downloadSize);
|
||||||
}
|
}
|
||||||
@ -157,7 +156,7 @@ namespace ix
|
|||||||
if (!lineValid)
|
if (!lineValid)
|
||||||
{
|
{
|
||||||
std::string errorMsg("Cannot retrieve status line");
|
std::string errorMsg("Cannot retrieve status line");
|
||||||
return std::make_tuple(code, HttpErrorCode_CannotReadStatusLine,
|
return std::make_tuple(code, HttpErrorCode::CannotReadStatusLine,
|
||||||
headers, payload, errorMsg,
|
headers, payload, errorMsg,
|
||||||
uploadSize, downloadSize);
|
uploadSize, downloadSize);
|
||||||
}
|
}
|
||||||
@ -172,7 +171,7 @@ namespace ix
|
|||||||
if (sscanf(line.c_str(), "HTTP/1.1 %d", &code) != 1)
|
if (sscanf(line.c_str(), "HTTP/1.1 %d", &code) != 1)
|
||||||
{
|
{
|
||||||
std::string errorMsg("Cannot parse response code from status line");
|
std::string errorMsg("Cannot parse response code from status line");
|
||||||
return std::make_tuple(code, HttpErrorCode_MissingStatus,
|
return std::make_tuple(code, HttpErrorCode::MissingStatus,
|
||||||
headers, payload, errorMsg,
|
headers, payload, errorMsg,
|
||||||
uploadSize, downloadSize);
|
uploadSize, downloadSize);
|
||||||
}
|
}
|
||||||
@ -184,7 +183,7 @@ namespace ix
|
|||||||
if (!headersValid)
|
if (!headersValid)
|
||||||
{
|
{
|
||||||
std::string errorMsg("Cannot parse http headers");
|
std::string errorMsg("Cannot parse http headers");
|
||||||
return std::make_tuple(code, HttpErrorCode_HeaderParsingError,
|
return std::make_tuple(code, HttpErrorCode::HeaderParsingError,
|
||||||
headers, payload, errorMsg,
|
headers, payload, errorMsg,
|
||||||
uploadSize, downloadSize);
|
uploadSize, downloadSize);
|
||||||
}
|
}
|
||||||
@ -195,7 +194,7 @@ namespace ix
|
|||||||
if (headers.find("Location") == headers.end())
|
if (headers.find("Location") == headers.end())
|
||||||
{
|
{
|
||||||
std::string errorMsg("Missing location header for redirect");
|
std::string errorMsg("Missing location header for redirect");
|
||||||
return std::make_tuple(code, HttpErrorCode_MissingLocation,
|
return std::make_tuple(code, HttpErrorCode::MissingLocation,
|
||||||
headers, payload, errorMsg,
|
headers, payload, errorMsg,
|
||||||
uploadSize, downloadSize);
|
uploadSize, downloadSize);
|
||||||
}
|
}
|
||||||
@ -204,7 +203,7 @@ namespace ix
|
|||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << "Too many redirects: " << redirects;
|
ss << "Too many redirects: " << redirects;
|
||||||
return std::make_tuple(code, HttpErrorCode_TooManyRedirects,
|
return std::make_tuple(code, HttpErrorCode::TooManyRedirects,
|
||||||
headers, payload, ss.str(),
|
headers, payload, ss.str(),
|
||||||
uploadSize, downloadSize);
|
uploadSize, downloadSize);
|
||||||
}
|
}
|
||||||
@ -216,7 +215,7 @@ namespace ix
|
|||||||
|
|
||||||
if (verb == "HEAD")
|
if (verb == "HEAD")
|
||||||
{
|
{
|
||||||
return std::make_tuple(code, HttpErrorCode_Ok,
|
return std::make_tuple(code, HttpErrorCode::Ok,
|
||||||
headers, payload, std::string(),
|
headers, payload, std::string(),
|
||||||
uploadSize, downloadSize);
|
uploadSize, downloadSize);
|
||||||
}
|
}
|
||||||
@ -237,7 +236,7 @@ namespace ix
|
|||||||
if (!chunkResult.first)
|
if (!chunkResult.first)
|
||||||
{
|
{
|
||||||
errorMsg = "Cannot read chunk";
|
errorMsg = "Cannot read chunk";
|
||||||
return std::make_tuple(code, HttpErrorCode_ChunkReadError,
|
return std::make_tuple(code, HttpErrorCode::ChunkReadError,
|
||||||
headers, payload, errorMsg,
|
headers, payload, errorMsg,
|
||||||
uploadSize, downloadSize);
|
uploadSize, downloadSize);
|
||||||
}
|
}
|
||||||
@ -255,7 +254,7 @@ namespace ix
|
|||||||
|
|
||||||
if (!lineResult.first)
|
if (!lineResult.first)
|
||||||
{
|
{
|
||||||
return std::make_tuple(code, HttpErrorCode_ChunkReadError,
|
return std::make_tuple(code, HttpErrorCode::ChunkReadError,
|
||||||
headers, payload, errorMsg,
|
headers, payload, errorMsg,
|
||||||
uploadSize, downloadSize);
|
uploadSize, downloadSize);
|
||||||
}
|
}
|
||||||
@ -282,7 +281,7 @@ namespace ix
|
|||||||
if (!chunkResult.first)
|
if (!chunkResult.first)
|
||||||
{
|
{
|
||||||
errorMsg = "Cannot read chunk";
|
errorMsg = "Cannot read chunk";
|
||||||
return std::make_tuple(code, HttpErrorCode_ChunkReadError,
|
return std::make_tuple(code, HttpErrorCode::ChunkReadError,
|
||||||
headers, payload, errorMsg,
|
headers, payload, errorMsg,
|
||||||
uploadSize, downloadSize);
|
uploadSize, downloadSize);
|
||||||
}
|
}
|
||||||
@ -293,7 +292,7 @@ namespace ix
|
|||||||
|
|
||||||
if (!lineResult.first)
|
if (!lineResult.first)
|
||||||
{
|
{
|
||||||
return std::make_tuple(code, HttpErrorCode_ChunkReadError,
|
return std::make_tuple(code, HttpErrorCode::ChunkReadError,
|
||||||
headers, payload, errorMsg,
|
headers, payload, errorMsg,
|
||||||
uploadSize, downloadSize);
|
uploadSize, downloadSize);
|
||||||
}
|
}
|
||||||
@ -308,7 +307,7 @@ namespace ix
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
std::string errorMsg("Cannot read http body");
|
std::string errorMsg("Cannot read http body");
|
||||||
return std::make_tuple(code, HttpErrorCode_CannotReadBody,
|
return std::make_tuple(code, HttpErrorCode::CannotReadBody,
|
||||||
headers, payload, errorMsg,
|
headers, payload, errorMsg,
|
||||||
uploadSize, downloadSize);
|
uploadSize, downloadSize);
|
||||||
}
|
}
|
||||||
@ -322,14 +321,14 @@ namespace ix
|
|||||||
if (!gzipInflate(payload, decompressedPayload))
|
if (!gzipInflate(payload, decompressedPayload))
|
||||||
{
|
{
|
||||||
std::string errorMsg("Error decompressing payload");
|
std::string errorMsg("Error decompressing payload");
|
||||||
return std::make_tuple(code, HttpErrorCode_Gzip,
|
return std::make_tuple(code, HttpErrorCode::Gzip,
|
||||||
headers, payload, errorMsg,
|
headers, payload, errorMsg,
|
||||||
uploadSize, downloadSize);
|
uploadSize, downloadSize);
|
||||||
}
|
}
|
||||||
payload = decompressedPayload;
|
payload = decompressedPayload;
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::make_tuple(code, HttpErrorCode_Ok,
|
return std::make_tuple(code, HttpErrorCode::Ok,
|
||||||
headers, payload, std::string(),
|
headers, payload, std::string(),
|
||||||
uploadSize, downloadSize);
|
uploadSize, downloadSize);
|
||||||
}
|
}
|
||||||
|
@ -19,23 +19,23 @@
|
|||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
enum HttpErrorCode
|
enum class HttpErrorCode
|
||||||
{
|
{
|
||||||
HttpErrorCode_Ok = 0,
|
Ok = 0,
|
||||||
HttpErrorCode_CannotConnect = 1,
|
CannotConnect = 1,
|
||||||
HttpErrorCode_Timeout = 2,
|
Timeout = 2,
|
||||||
HttpErrorCode_Gzip = 3,
|
Gzip = 3,
|
||||||
HttpErrorCode_UrlMalformed = 4,
|
UrlMalformed = 4,
|
||||||
HttpErrorCode_CannotCreateSocket = 5,
|
CannotCreateSocket = 5,
|
||||||
HttpErrorCode_SendError = 6,
|
SendError = 6,
|
||||||
HttpErrorCode_ReadError = 7,
|
ReadError = 7,
|
||||||
HttpErrorCode_CannotReadStatusLine = 8,
|
CannotReadStatusLine = 8,
|
||||||
HttpErrorCode_MissingStatus = 9,
|
MissingStatus = 9,
|
||||||
HttpErrorCode_HeaderParsingError = 10,
|
HeaderParsingError = 10,
|
||||||
HttpErrorCode_MissingLocation = 11,
|
MissingLocation = 11,
|
||||||
HttpErrorCode_TooManyRedirects = 12,
|
TooManyRedirects = 12,
|
||||||
HttpErrorCode_ChunkReadError = 13,
|
ChunkReadError = 13,
|
||||||
HttpErrorCode_CannotReadBody = 14
|
CannotReadBody = 14
|
||||||
};
|
};
|
||||||
|
|
||||||
using HttpResponse = std::tuple<int, // status
|
using HttpResponse = std::tuple<int, // status
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* IXNetSystem.cpp
|
* IXNetSystem.cpp
|
||||||
* Author: Benjamin Sergeant
|
* Author: Korchynskyi Dmytro
|
||||||
* Copyright (c) 2019 Machine Zone. All rights reserved.
|
* Copyright (c) 2019 Machine Zone. All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -40,6 +40,8 @@ namespace ix
|
|||||||
|
|
||||||
bool SelectInterruptPipe::init(std::string& errorMsg)
|
bool SelectInterruptPipe::init(std::string& errorMsg)
|
||||||
{
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(_fildesMutex);
|
||||||
|
|
||||||
// calling init twice is a programming error
|
// calling init twice is a programming error
|
||||||
assert(_fildes[kPipeReadIndex] == -1);
|
assert(_fildes[kPipeReadIndex] == -1);
|
||||||
assert(_fildes[kPipeWriteIndex] == -1);
|
assert(_fildes[kPipeWriteIndex] == -1);
|
||||||
@ -108,6 +110,8 @@ namespace ix
|
|||||||
|
|
||||||
bool SelectInterruptPipe::notify(uint64_t value)
|
bool SelectInterruptPipe::notify(uint64_t value)
|
||||||
{
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(_fildesMutex);
|
||||||
|
|
||||||
int fd = _fildes[kPipeWriteIndex];
|
int fd = _fildes[kPipeWriteIndex];
|
||||||
if (fd == -1) return false;
|
if (fd == -1) return false;
|
||||||
|
|
||||||
@ -118,6 +122,8 @@ namespace ix
|
|||||||
// TODO: return max uint64_t for errors ?
|
// TODO: return max uint64_t for errors ?
|
||||||
uint64_t SelectInterruptPipe::read()
|
uint64_t SelectInterruptPipe::read()
|
||||||
{
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(_fildesMutex);
|
||||||
|
|
||||||
int fd = _fildes[kPipeReadIndex];
|
int fd = _fildes[kPipeReadIndex];
|
||||||
|
|
||||||
uint64_t value = 0;
|
uint64_t value = 0;
|
||||||
@ -133,6 +139,8 @@ namespace ix
|
|||||||
|
|
||||||
int SelectInterruptPipe::getFd() const
|
int SelectInterruptPipe::getFd() const
|
||||||
{
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(_fildesMutex);
|
||||||
|
|
||||||
return _fildes[kPipeReadIndex];
|
return _fildes[kPipeReadIndex];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
@ -30,6 +31,7 @@ namespace ix
|
|||||||
// happens between a control thread and a background thread, which is
|
// happens between a control thread and a background thread, which is
|
||||||
// blocked on select.
|
// blocked on select.
|
||||||
int _fildes[2];
|
int _fildes[2];
|
||||||
|
mutable std::mutex _fildesMutex;
|
||||||
|
|
||||||
// Used to identify the read/write idx
|
// Used to identify the read/write idx
|
||||||
static const int kPipeReadIndex;
|
static const int kPipeReadIndex;
|
||||||
|
@ -19,7 +19,6 @@
|
|||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
#ifdef min
|
#ifdef min
|
||||||
#undef min
|
#undef min
|
||||||
@ -45,14 +44,14 @@ namespace ix
|
|||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
PollResultType Socket::poll(int timeoutSecs)
|
PollResultType Socket::poll(int timeoutMs)
|
||||||
{
|
{
|
||||||
if (_sockfd == -1)
|
if (_sockfd == -1)
|
||||||
{
|
{
|
||||||
return PollResultType::Error;
|
return PollResultType::Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
return isReadyToRead(1000 * timeoutSecs);
|
return isReadyToRead(timeoutMs);
|
||||||
}
|
}
|
||||||
|
|
||||||
PollResultType Socket::select(bool readyToRead, int timeoutMs)
|
PollResultType Socket::select(bool readyToRead, int timeoutMs)
|
||||||
@ -160,8 +159,6 @@ namespace ix
|
|||||||
|
|
||||||
ssize_t Socket::send(char* buffer, size_t length)
|
ssize_t Socket::send(char* buffer, size_t length)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(_socketMutex);
|
|
||||||
|
|
||||||
int flags = 0;
|
int flags = 0;
|
||||||
#ifdef MSG_NOSIGNAL
|
#ifdef MSG_NOSIGNAL
|
||||||
flags = MSG_NOSIGNAL;
|
flags = MSG_NOSIGNAL;
|
||||||
@ -177,8 +174,6 @@ namespace ix
|
|||||||
|
|
||||||
ssize_t Socket::recv(void* buffer, size_t length)
|
ssize_t Socket::recv(void* buffer, size_t length)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(_socketMutex);
|
|
||||||
|
|
||||||
int flags = 0;
|
int flags = 0;
|
||||||
#ifdef MSG_NOSIGNAL
|
#ifdef MSG_NOSIGNAL
|
||||||
flags = MSG_NOSIGNAL;
|
flags = MSG_NOSIGNAL;
|
||||||
|
@ -56,7 +56,7 @@ namespace ix
|
|||||||
bool init(std::string& errorMsg);
|
bool init(std::string& errorMsg);
|
||||||
|
|
||||||
// Functions to check whether there is activity on the socket
|
// Functions to check whether there is activity on the socket
|
||||||
PollResultType poll(int timeoutSecs = kDefaultPollTimeout);
|
PollResultType poll(int timeoutMs = kDefaultPollTimeout);
|
||||||
bool wakeUpFromPoll(uint8_t wakeUpCode);
|
bool wakeUpFromPoll(uint8_t wakeUpCode);
|
||||||
|
|
||||||
PollResultType isReadyToWrite(int timeoutMs);
|
PollResultType isReadyToWrite(int timeoutMs);
|
||||||
|
@ -20,8 +20,6 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#define socketerrno errno
|
#define socketerrno errno
|
||||||
|
|
||||||
|
@ -10,7 +10,6 @@
|
|||||||
#include "IXSocketConnect.h"
|
#include "IXSocketConnect.h"
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
#include <openssl/x509v3.h>
|
#include <openssl/x509v3.h>
|
||||||
|
|
||||||
|
@ -5,43 +5,30 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "IXUrlParser.h"
|
#include "IXUrlParser.h"
|
||||||
|
#include "LUrlParser.h"
|
||||||
#include <iostream>
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
//
|
|
||||||
// The only difference between those 2 regex is the protocol
|
|
||||||
//
|
|
||||||
std::regex UrlParser::_httpRegex("(http|https)://([^/ :]+):?([^/ ]*)(/?[^ #?]*)\\x3f?([^ #]*)#?([^ ]*)");
|
|
||||||
std::regex UrlParser::_webSocketRegex("(ws|wss)://([^/ :]+):?([^/ ]*)(/?[^ #?]*)\\x3f?([^ #]*)#?([^ ]*)");
|
|
||||||
|
|
||||||
bool UrlParser::parse(const std::string& url,
|
bool UrlParser::parse(const std::string& url,
|
||||||
std::string& protocol,
|
std::string& protocol,
|
||||||
std::string& host,
|
std::string& host,
|
||||||
std::string& path,
|
std::string& path,
|
||||||
std::string& query,
|
std::string& query,
|
||||||
int& port,
|
int& port)
|
||||||
bool websocket)
|
|
||||||
{
|
{
|
||||||
std::cmatch what;
|
LUrlParser::clParseURL res = LUrlParser::clParseURL::ParseURL(url);
|
||||||
if (!regex_match(url.c_str(), what,
|
|
||||||
websocket ? _webSocketRegex : _httpRegex))
|
if (!res.IsValid())
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string portStr;
|
protocol = res.m_Scheme;
|
||||||
|
host = res.m_Host;
|
||||||
|
path = res.m_Path;
|
||||||
|
query = res.m_Query;
|
||||||
|
|
||||||
protocol = std::string(what[1].first, what[1].second);
|
if (!res.GetPort(&port))
|
||||||
host = std::string(what[2].first, what[2].second);
|
|
||||||
portStr = std::string(what[3].first, what[3].second);
|
|
||||||
path = std::string(what[4].first, what[4].second);
|
|
||||||
query = std::string(what[5].first, what[5].second);
|
|
||||||
|
|
||||||
if (portStr.empty())
|
|
||||||
{
|
{
|
||||||
if (protocol == "ws" || protocol == "http")
|
if (protocol == "ws" || protocol == "http")
|
||||||
{
|
{
|
||||||
@ -58,12 +45,6 @@ namespace ix
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
std::stringstream ss;
|
|
||||||
ss << portStr;
|
|
||||||
ss >> port;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (path.empty())
|
if (path.empty())
|
||||||
{
|
{
|
||||||
@ -83,22 +64,4 @@ namespace ix
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void UrlParser::printUrl(const std::string& url, bool websocket)
|
|
||||||
{
|
|
||||||
std::string protocol, host, path, query;
|
|
||||||
int port {0};
|
|
||||||
|
|
||||||
if (!parse(url, protocol, host, path, query, port, websocket))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << "[" << url << "]" << std::endl;
|
|
||||||
std::cout << protocol << std::endl;
|
|
||||||
std::cout << host << std::endl;
|
|
||||||
std::cout << port << std::endl;
|
|
||||||
std::cout << path << std::endl;
|
|
||||||
std::cout << query << std::endl;
|
|
||||||
std::cout << "-------------------------------" << std::endl;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <regex>
|
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
@ -19,13 +18,6 @@ namespace ix
|
|||||||
std::string& host,
|
std::string& host,
|
||||||
std::string& path,
|
std::string& path,
|
||||||
std::string& query,
|
std::string& query,
|
||||||
int& port,
|
int& port);
|
||||||
bool websocket);
|
|
||||||
|
|
||||||
static void printUrl(const std::string& url, bool websocket);
|
|
||||||
|
|
||||||
private:
|
|
||||||
static std::regex _httpRegex;
|
|
||||||
static std::regex _webSocketRegex;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -8,22 +8,26 @@
|
|||||||
#include "IXSetThreadName.h"
|
#include "IXSetThreadName.h"
|
||||||
#include "IXWebSocketHandshake.h"
|
#include "IXWebSocketHandshake.h"
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
uint64_t calculateRetryWaitMilliseconds(uint64_t retry_count)
|
uint64_t calculateRetryWaitMilliseconds(uint32_t retry_count)
|
||||||
{
|
{
|
||||||
// This will overflow quite fast for large value of retry_count
|
uint64_t wait_time;
|
||||||
// and will become 0, in which case the wait time will be none
|
|
||||||
// and we'll be constantly retrying to connect.
|
|
||||||
uint64_t wait_time = ((uint64_t) std::pow(2, retry_count) * 100L);
|
|
||||||
|
|
||||||
// cap the wait time to 10s, or to retry_count == 10 for which wait_time > 10s
|
if (retry_count <= 6)
|
||||||
uint64_t tenSeconds = 10 * 1000;
|
{
|
||||||
return (wait_time > tenSeconds || retry_count > 10) ? tenSeconds : wait_time;
|
// max wait_time is 6400 ms (2 ^ 6 = 64)
|
||||||
|
wait_time = ((uint64_t)std::pow(2, retry_count) * 100L);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
wait_time = 10 * 1000; // 10 sec
|
||||||
|
}
|
||||||
|
|
||||||
|
return wait_time;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,7 +51,7 @@ namespace ix
|
|||||||
_ws.setOnCloseCallback(
|
_ws.setOnCloseCallback(
|
||||||
[this](uint16_t code, const std::string& reason, size_t wireSize, bool remote)
|
[this](uint16_t code, const std::string& reason, size_t wireSize, bool remote)
|
||||||
{
|
{
|
||||||
_onMessageCallback(WebSocket_MessageType_Close, "", wireSize,
|
_onMessageCallback(WebSocketMessageType::Close, "", wireSize,
|
||||||
WebSocketErrorInfo(), WebSocketOpenInfo(),
|
WebSocketErrorInfo(), WebSocketOpenInfo(),
|
||||||
WebSocketCloseInfo(code, reason, remote));
|
WebSocketCloseInfo(code, reason, remote));
|
||||||
}
|
}
|
||||||
@ -140,24 +144,16 @@ namespace ix
|
|||||||
|
|
||||||
void WebSocket::stop()
|
void WebSocket::stop()
|
||||||
{
|
{
|
||||||
bool automaticReconnection = _automaticReconnection;
|
|
||||||
|
|
||||||
// This value needs to be forced when shutting down, it is restored later
|
|
||||||
_automaticReconnection = false;
|
|
||||||
|
|
||||||
close();
|
close();
|
||||||
|
|
||||||
if (!_thread.joinable())
|
if (_thread.joinable())
|
||||||
{
|
{
|
||||||
_automaticReconnection = automaticReconnection;
|
// wait until working thread will exit
|
||||||
return;
|
// it will exit after close operation is finished
|
||||||
}
|
|
||||||
|
|
||||||
_stop = true;
|
_stop = true;
|
||||||
_thread.join();
|
_thread.join();
|
||||||
_stop = false;
|
_stop = false;
|
||||||
|
}
|
||||||
_automaticReconnection = automaticReconnection;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
WebSocketInitResult WebSocket::connect(int timeoutSecs)
|
WebSocketInitResult WebSocket::connect(int timeoutSecs)
|
||||||
@ -176,7 +172,7 @@ namespace ix
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
_onMessageCallback(WebSocket_MessageType_Open, "", 0,
|
_onMessageCallback(WebSocketMessageType::Open, "", 0,
|
||||||
WebSocketErrorInfo(),
|
WebSocketErrorInfo(),
|
||||||
WebSocketOpenInfo(status.uri, status.headers),
|
WebSocketOpenInfo(status.uri, status.headers),
|
||||||
WebSocketCloseInfo());
|
WebSocketCloseInfo());
|
||||||
@ -199,7 +195,7 @@ namespace ix
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
_onMessageCallback(WebSocket_MessageType_Open, "", 0,
|
_onMessageCallback(WebSocketMessageType::Open, "", 0,
|
||||||
WebSocketErrorInfo(),
|
WebSocketErrorInfo(),
|
||||||
WebSocketOpenInfo(status.uri, status.headers),
|
WebSocketOpenInfo(status.uri, status.headers),
|
||||||
WebSocketCloseInfo());
|
WebSocketCloseInfo());
|
||||||
@ -208,12 +204,12 @@ namespace ix
|
|||||||
|
|
||||||
bool WebSocket::isConnected() const
|
bool WebSocket::isConnected() const
|
||||||
{
|
{
|
||||||
return getReadyState() == WebSocket_ReadyState_Open;
|
return getReadyState() == ReadyState::Open;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WebSocket::isClosing() const
|
bool WebSocket::isClosing() const
|
||||||
{
|
{
|
||||||
return getReadyState() == WebSocket_ReadyState_Closing;
|
return getReadyState() == ReadyState::Closing;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebSocket::close()
|
void WebSocket::close()
|
||||||
@ -221,62 +217,58 @@ namespace ix
|
|||||||
_ws.close();
|
_ws.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebSocket::reconnectPerpetuallyIfDisconnected()
|
void WebSocket::checkConnection(bool firstConnectionAttempt)
|
||||||
{
|
{
|
||||||
uint64_t retries = 0;
|
|
||||||
WebSocketErrorInfo connectErr;
|
|
||||||
ix::WebSocketInitResult status;
|
|
||||||
using millis = std::chrono::duration<double, std::milli>;
|
using millis = std::chrono::duration<double, std::milli>;
|
||||||
|
|
||||||
|
uint32_t retries = 0;
|
||||||
millis duration;
|
millis duration;
|
||||||
|
ix::WebSocketInitResult status;
|
||||||
|
|
||||||
// Try to connect only once when we don't have automaticReconnection setup
|
// Try to connect perpertually
|
||||||
if (!isConnected() && !isClosing() && !_stop && !_automaticReconnection)
|
|
||||||
{
|
|
||||||
status = connect(_handshakeTimeoutSecs);
|
|
||||||
|
|
||||||
if (!status.success)
|
|
||||||
{
|
|
||||||
duration = millis(calculateRetryWaitMilliseconds(retries++));
|
|
||||||
|
|
||||||
connectErr.retries = retries;
|
|
||||||
connectErr.wait_time = duration.count();
|
|
||||||
connectErr.reason = status.errorStr;
|
|
||||||
connectErr.http_status = status.http_status;
|
|
||||||
_onMessageCallback(WebSocket_MessageType_Error, "", 0,
|
|
||||||
connectErr, WebSocketOpenInfo(),
|
|
||||||
WebSocketCloseInfo());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Otherwise try to reconnect perpertually
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
if (isConnected() || isClosing() || _stop || !_automaticReconnection)
|
if (isConnected() || isClosing() || _stop)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!firstConnectionAttempt && !_automaticReconnection)
|
||||||
|
{
|
||||||
|
// Do not attempt to reconnect
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
firstConnectionAttempt = false;
|
||||||
|
|
||||||
|
// Only sleep if we are retrying
|
||||||
|
if (duration.count() > 0)
|
||||||
|
{
|
||||||
|
// to do: make sleeping conditional
|
||||||
|
std::this_thread::sleep_for(duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to connect synchronously
|
||||||
status = connect(_handshakeTimeoutSecs);
|
status = connect(_handshakeTimeoutSecs);
|
||||||
|
|
||||||
if (!status.success)
|
if (!status.success)
|
||||||
|
{
|
||||||
|
WebSocketErrorInfo connectErr;
|
||||||
|
|
||||||
|
if (_automaticReconnection)
|
||||||
{
|
{
|
||||||
duration = millis(calculateRetryWaitMilliseconds(retries++));
|
duration = millis(calculateRetryWaitMilliseconds(retries++));
|
||||||
|
|
||||||
connectErr.retries = retries;
|
|
||||||
connectErr.wait_time = duration.count();
|
connectErr.wait_time = duration.count();
|
||||||
|
connectErr.retries = retries;
|
||||||
|
}
|
||||||
|
|
||||||
connectErr.reason = status.errorStr;
|
connectErr.reason = status.errorStr;
|
||||||
connectErr.http_status = status.http_status;
|
connectErr.http_status = status.http_status;
|
||||||
_onMessageCallback(WebSocket_MessageType_Error, "", 0,
|
|
||||||
|
_onMessageCallback(WebSocketMessageType::Error, "", 0,
|
||||||
connectErr, WebSocketOpenInfo(),
|
connectErr, WebSocketOpenInfo(),
|
||||||
WebSocketCloseInfo());
|
WebSocketCloseInfo());
|
||||||
|
|
||||||
// Only sleep if we aren't in the middle of stopping
|
|
||||||
if (!_stop)
|
|
||||||
{
|
|
||||||
std::this_thread::sleep_for(duration);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -285,22 +277,27 @@ namespace ix
|
|||||||
{
|
{
|
||||||
setThreadName(getUrl());
|
setThreadName(getUrl());
|
||||||
|
|
||||||
|
bool firstConnectionAttempt = true;
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
if (_stop) return;
|
|
||||||
|
|
||||||
// 1. Make sure we are always connected
|
// 1. Make sure we are always connected
|
||||||
reconnectPerpetuallyIfDisconnected();
|
checkConnection(firstConnectionAttempt);
|
||||||
|
|
||||||
if (_stop) return;
|
firstConnectionAttempt = false;
|
||||||
|
|
||||||
|
// if here we are closed then checkConnection was not able to connect
|
||||||
|
if (getReadyState() == ReadyState::Closed)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// 2. Poll to see if there's any new data available
|
// 2. Poll to see if there's any new data available
|
||||||
_ws.poll();
|
WebSocketTransport::PollResult pollResult = _ws.poll();
|
||||||
|
|
||||||
if (_stop) return;
|
|
||||||
|
|
||||||
// 3. Dispatch the incoming messages
|
// 3. Dispatch the incoming messages
|
||||||
_ws.dispatch(
|
_ws.dispatch(
|
||||||
|
pollResult,
|
||||||
[this](const std::string& msg,
|
[this](const std::string& msg,
|
||||||
size_t wireSize,
|
size_t wireSize,
|
||||||
bool decompressionError,
|
bool decompressionError,
|
||||||
@ -309,24 +306,25 @@ namespace ix
|
|||||||
WebSocketMessageType webSocketMessageType;
|
WebSocketMessageType webSocketMessageType;
|
||||||
switch (messageKind)
|
switch (messageKind)
|
||||||
{
|
{
|
||||||
case WebSocketTransport::MSG:
|
default:
|
||||||
|
case WebSocketTransport::MessageKind::MSG:
|
||||||
{
|
{
|
||||||
webSocketMessageType = WebSocket_MessageType_Message;
|
webSocketMessageType = WebSocketMessageType::Message;
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case WebSocketTransport::PING:
|
case WebSocketTransport::MessageKind::PING:
|
||||||
{
|
{
|
||||||
webSocketMessageType = WebSocket_MessageType_Ping;
|
webSocketMessageType = WebSocketMessageType::Ping;
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case WebSocketTransport::PONG:
|
case WebSocketTransport::MessageKind::PONG:
|
||||||
{
|
{
|
||||||
webSocketMessageType = WebSocket_MessageType_Pong;
|
webSocketMessageType = WebSocketMessageType::Pong;
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case WebSocketTransport::FRAGMENT:
|
case WebSocketTransport::MessageKind::FRAGMENT:
|
||||||
{
|
{
|
||||||
webSocketMessageType = WebSocket_MessageType_Fragment;
|
webSocketMessageType = WebSocketMessageType::Fragment;
|
||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -339,9 +337,6 @@ namespace ix
|
|||||||
|
|
||||||
WebSocket::invokeTrafficTrackerCallback(msg.size(), true);
|
WebSocket::invokeTrafficTrackerCallback(msg.size(), true);
|
||||||
});
|
});
|
||||||
|
|
||||||
// If we aren't trying to reconnect automatically, exit if we aren't connected
|
|
||||||
if (!isConnected() && !_automaticReconnection) return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -368,10 +363,10 @@ namespace ix
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
WebSocketSendInfo WebSocket::send(const std::string& text,
|
WebSocketSendInfo WebSocket::send(const std::string& data,
|
||||||
const OnProgressCallback& onProgressCallback)
|
const OnProgressCallback& onProgressCallback)
|
||||||
{
|
{
|
||||||
return sendMessage(text, SendMessageKind::Binary, onProgressCallback);
|
return sendMessage(data, SendMessageKind::Binary, onProgressCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
WebSocketSendInfo WebSocket::sendText(const std::string& text,
|
WebSocketSendInfo WebSocket::sendText(const std::string& text,
|
||||||
@ -434,11 +429,11 @@ namespace ix
|
|||||||
{
|
{
|
||||||
switch (_ws.getReadyState())
|
switch (_ws.getReadyState())
|
||||||
{
|
{
|
||||||
case ix::WebSocketTransport::OPEN: return WebSocket_ReadyState_Open;
|
case ix::WebSocketTransport::ReadyState::OPEN : return ReadyState::Open;
|
||||||
case ix::WebSocketTransport::CONNECTING: return WebSocket_ReadyState_Connecting;
|
case ix::WebSocketTransport::ReadyState::CONNECTING: return ReadyState::Connecting;
|
||||||
case ix::WebSocketTransport::CLOSING: return WebSocket_ReadyState_Closing;
|
case ix::WebSocketTransport::ReadyState::CLOSING : return ReadyState::Closing;
|
||||||
case ix::WebSocketTransport::CLOSED: return WebSocket_ReadyState_Closed;
|
case ix::WebSocketTransport::ReadyState::CLOSED : return ReadyState::Closed;
|
||||||
default: return WebSocket_ReadyState_Closed;
|
default: return ReadyState::Closed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -446,11 +441,11 @@ namespace ix
|
|||||||
{
|
{
|
||||||
switch (readyState)
|
switch (readyState)
|
||||||
{
|
{
|
||||||
case WebSocket_ReadyState_Open: return "OPEN";
|
case ReadyState::Open : return "OPEN";
|
||||||
case WebSocket_ReadyState_Connecting: return "CONNECTING";
|
case ReadyState::Connecting: return "CONNECTING";
|
||||||
case WebSocket_ReadyState_Closing: return "CLOSING";
|
case ReadyState::Closing : return "CLOSING";
|
||||||
case WebSocket_ReadyState_Closed: return "CLOSED";
|
case ReadyState::Closed : return "CLOSED";
|
||||||
default: return "CLOSED";
|
default: return "UNKNOWN";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,23 +24,23 @@
|
|||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/API/WebSocket#Ready_state_constants
|
// https://developer.mozilla.org/en-US/docs/Web/API/WebSocket#Ready_state_constants
|
||||||
enum ReadyState
|
enum class ReadyState
|
||||||
{
|
{
|
||||||
WebSocket_ReadyState_Connecting = 0,
|
Connecting = 0,
|
||||||
WebSocket_ReadyState_Open = 1,
|
Open = 1,
|
||||||
WebSocket_ReadyState_Closing = 2,
|
Closing = 2,
|
||||||
WebSocket_ReadyState_Closed = 3
|
Closed = 3
|
||||||
};
|
};
|
||||||
|
|
||||||
enum WebSocketMessageType
|
enum class WebSocketMessageType
|
||||||
{
|
{
|
||||||
WebSocket_MessageType_Message = 0,
|
Message = 0,
|
||||||
WebSocket_MessageType_Open = 1,
|
Open = 1,
|
||||||
WebSocket_MessageType_Close = 2,
|
Close = 2,
|
||||||
WebSocket_MessageType_Error = 3,
|
Error = 3,
|
||||||
WebSocket_MessageType_Ping = 4,
|
Ping = 4,
|
||||||
WebSocket_MessageType_Pong = 5,
|
Pong = 5,
|
||||||
WebSocket_MessageType_Fragment = 6
|
Fragment = 6
|
||||||
};
|
};
|
||||||
|
|
||||||
struct WebSocketOpenInfo
|
struct WebSocketOpenInfo
|
||||||
@ -91,7 +91,6 @@ namespace ix
|
|||||||
|
|
||||||
void setUrl(const std::string& url);
|
void setUrl(const std::string& url);
|
||||||
void setPerMessageDeflateOptions(const WebSocketPerMessageDeflateOptions& perMessageDeflateOptions);
|
void setPerMessageDeflateOptions(const WebSocketPerMessageDeflateOptions& perMessageDeflateOptions);
|
||||||
void setHandshakeTimeout(int handshakeTimeoutSecs);
|
|
||||||
void setHeartBeatPeriod(int heartBeatPeriodSecs);
|
void setHeartBeatPeriod(int heartBeatPeriodSecs);
|
||||||
void setPingInterval(int pingIntervalSecs); // alias of setHeartBeatPeriod
|
void setPingInterval(int pingIntervalSecs); // alias of setHeartBeatPeriod
|
||||||
void setPingTimeout(int pingTimeoutSecs);
|
void setPingTimeout(int pingTimeoutSecs);
|
||||||
@ -100,13 +99,15 @@ namespace ix
|
|||||||
|
|
||||||
// Run asynchronously, by calling start and stop.
|
// Run asynchronously, by calling start and stop.
|
||||||
void start();
|
void start();
|
||||||
|
// stop is synchronous
|
||||||
void stop();
|
void stop();
|
||||||
|
|
||||||
// Run in blocking mode, by connecting first manually, and then calling run.
|
// Run in blocking mode, by connecting first manually, and then calling run.
|
||||||
WebSocketInitResult connect(int timeoutSecs);
|
WebSocketInitResult connect(int timeoutSecs);
|
||||||
void run();
|
void run();
|
||||||
|
|
||||||
WebSocketSendInfo send(const std::string& text,
|
// send binary data
|
||||||
|
WebSocketSendInfo send(const std::string& data,
|
||||||
const OnProgressCallback& onProgressCallback = nullptr);
|
const OnProgressCallback& onProgressCallback = nullptr);
|
||||||
WebSocketSendInfo sendText(const std::string& text,
|
WebSocketSendInfo sendText(const std::string& text,
|
||||||
const OnProgressCallback& onProgressCallback = nullptr);
|
const OnProgressCallback& onProgressCallback = nullptr);
|
||||||
@ -118,6 +119,8 @@ namespace ix
|
|||||||
static void resetTrafficTrackerCallback();
|
static void resetTrafficTrackerCallback();
|
||||||
|
|
||||||
ReadyState getReadyState() const;
|
ReadyState getReadyState() const;
|
||||||
|
static std::string readyStateToString(ReadyState readyState);
|
||||||
|
|
||||||
const std::string& getUrl() const;
|
const std::string& getUrl() const;
|
||||||
const WebSocketPerMessageDeflateOptions& getPerMessageDeflateOptions() const;
|
const WebSocketPerMessageDeflateOptions& getPerMessageDeflateOptions() const;
|
||||||
int getHeartBeatPeriod() const;
|
int getHeartBeatPeriod() const;
|
||||||
@ -136,12 +139,10 @@ namespace ix
|
|||||||
|
|
||||||
bool isConnected() const;
|
bool isConnected() const;
|
||||||
bool isClosing() const;
|
bool isClosing() const;
|
||||||
void reconnectPerpetuallyIfDisconnected();
|
void checkConnection(bool firstConnectionAttempt);
|
||||||
std::string readyStateToString(ReadyState readyState);
|
|
||||||
static void invokeTrafficTrackerCallback(size_t size, bool incoming);
|
static void invokeTrafficTrackerCallback(size_t size, bool incoming);
|
||||||
|
|
||||||
// Server
|
// Server
|
||||||
void setSocketFileDescriptor(int fd);
|
|
||||||
WebSocketInitResult connectToSocket(int fd, int timeoutSecs);
|
WebSocketInitResult connectToSocket(int fd, int timeoutSecs);
|
||||||
|
|
||||||
WebSocketTransport _ws;
|
WebSocketTransport _ws;
|
||||||
|
@ -12,10 +12,10 @@ namespace ix
|
|||||||
{
|
{
|
||||||
struct WebSocketErrorInfo
|
struct WebSocketErrorInfo
|
||||||
{
|
{
|
||||||
uint64_t retries;
|
uint32_t retries = 0;
|
||||||
double wait_time;
|
double wait_time = 0;
|
||||||
int http_status;
|
int http_status = 0;
|
||||||
std::string reason;
|
std::string reason;
|
||||||
bool decompressionError;
|
bool decompressionError = false;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -10,9 +10,7 @@
|
|||||||
|
|
||||||
#include "libwshandshake.hpp"
|
#include "libwshandshake.hpp"
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <regex>
|
|
||||||
#include <random>
|
#include <random>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
#include "IXWebSocketPerMessageDeflateCodec.h"
|
#include "IXWebSocketPerMessageDeflateCodec.h"
|
||||||
#include "IXWebSocketPerMessageDeflateOptions.h"
|
#include "IXWebSocketPerMessageDeflateOptions.h"
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
#include "IXWebSocketPerMessageDeflateOptions.h"
|
#include "IXWebSocketPerMessageDeflateOptions.h"
|
||||||
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <iostream>
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
|
|
||||||
|
@ -23,7 +23,6 @@ namespace ix
|
|||||||
WebSocketPerMessageDeflateOptions(std::string extension);
|
WebSocketPerMessageDeflateOptions(std::string extension);
|
||||||
|
|
||||||
std::string generateHeader();
|
std::string generateHeader();
|
||||||
std::string parseHeader();
|
|
||||||
bool enabled() const;
|
bool enabled() const;
|
||||||
bool getClientNoContextTakeover() const;
|
bool getClientNoContextTakeover() const;
|
||||||
bool getServerNoContextTakeover() const;
|
bool getServerNoContextTakeover() const;
|
||||||
|
@ -6,9 +6,6 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
struct WebSocketSendInfo
|
struct WebSocketSendInfo
|
||||||
|
@ -45,7 +45,6 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <cstdarg>
|
#include <cstdarg>
|
||||||
#include <iostream>
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
@ -72,28 +71,34 @@ namespace ix
|
|||||||
const int WebSocketTransport::kDefaultPingIntervalSecs(-1);
|
const int WebSocketTransport::kDefaultPingIntervalSecs(-1);
|
||||||
const int WebSocketTransport::kDefaultPingTimeoutSecs(-1);
|
const int WebSocketTransport::kDefaultPingTimeoutSecs(-1);
|
||||||
const bool WebSocketTransport::kDefaultEnablePong(true);
|
const bool WebSocketTransport::kDefaultEnablePong(true);
|
||||||
|
const int WebSocketTransport::kClosingMaximumWaitingDelayInMs(200);
|
||||||
constexpr size_t WebSocketTransport::kChunkSize;
|
constexpr size_t WebSocketTransport::kChunkSize;
|
||||||
|
|
||||||
const uint16_t WebSocketTransport::kInternalErrorCode(1011);
|
const uint16_t WebSocketTransport::kInternalErrorCode(1011);
|
||||||
const uint16_t WebSocketTransport::kAbnormalCloseCode(1006);
|
const uint16_t WebSocketTransport::kAbnormalCloseCode(1006);
|
||||||
const uint16_t WebSocketTransport::kProtocolErrorCode(1002);
|
const uint16_t WebSocketTransport::kProtocolErrorCode(1002);
|
||||||
|
const uint16_t WebSocketTransport::kNoStatusCodeErrorCode(1005);
|
||||||
const std::string WebSocketTransport::kInternalErrorMessage("Internal error");
|
const std::string WebSocketTransport::kInternalErrorMessage("Internal error");
|
||||||
const std::string WebSocketTransport::kAbnormalCloseMessage("Abnormal closure");
|
const std::string WebSocketTransport::kAbnormalCloseMessage("Abnormal closure");
|
||||||
const std::string WebSocketTransport::kPingTimeoutMessage("Ping timeout");
|
const std::string WebSocketTransport::kPingTimeoutMessage("Ping timeout");
|
||||||
const std::string WebSocketTransport::kProtocolErrorMessage("Protocol error");
|
const std::string WebSocketTransport::kProtocolErrorMessage("Protocol error");
|
||||||
|
const std::string WebSocketTransport::kNoStatusCodeErrorMessage("No status code");
|
||||||
|
|
||||||
WebSocketTransport::WebSocketTransport() :
|
WebSocketTransport::WebSocketTransport() :
|
||||||
_useMask(true),
|
_useMask(true),
|
||||||
_readyState(CLOSED),
|
_readyState(ReadyState::CLOSED),
|
||||||
_closeCode(kInternalErrorCode),
|
_closeCode(kInternalErrorCode),
|
||||||
_closeReason(kInternalErrorMessage),
|
_closeReason(kInternalErrorMessage),
|
||||||
_closeWireSize(0),
|
_closeWireSize(0),
|
||||||
_closeRemote(false),
|
_closeRemote(false),
|
||||||
_enablePerMessageDeflate(false),
|
_enablePerMessageDeflate(false),
|
||||||
_requestInitCancellation(false),
|
_requestInitCancellation(false),
|
||||||
|
_closingTimePoint(std::chrono::steady_clock::now()),
|
||||||
_enablePong(kDefaultEnablePong),
|
_enablePong(kDefaultEnablePong),
|
||||||
_pingIntervalSecs(kDefaultPingIntervalSecs),
|
_pingIntervalSecs(kDefaultPingIntervalSecs),
|
||||||
_pingTimeoutSecs(kDefaultPingTimeoutSecs),
|
_pingTimeoutSecs(kDefaultPingTimeoutSecs),
|
||||||
_pingIntervalOrTimeoutGCDSecs(-1),
|
_pingIntervalOrTimeoutGCDSecs(-1),
|
||||||
|
_nextGCDTimePoint(std::chrono::steady_clock::now()),
|
||||||
_lastSendPingTimePoint(std::chrono::steady_clock::now()),
|
_lastSendPingTimePoint(std::chrono::steady_clock::now()),
|
||||||
_lastReceivePongTimePoint(std::chrono::steady_clock::now())
|
_lastReceivePongTimePoint(std::chrono::steady_clock::now())
|
||||||
{
|
{
|
||||||
@ -129,6 +134,11 @@ namespace ix
|
|||||||
{
|
{
|
||||||
_pingIntervalOrTimeoutGCDSecs = pingIntervalSecs;
|
_pingIntervalOrTimeoutGCDSecs = pingIntervalSecs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_pingIntervalOrTimeoutGCDSecs > 0)
|
||||||
|
{
|
||||||
|
_nextGCDTimePoint = std::chrono::steady_clock::now() + std::chrono::seconds(_pingIntervalOrTimeoutGCDSecs);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Client
|
// Client
|
||||||
@ -137,9 +147,8 @@ namespace ix
|
|||||||
{
|
{
|
||||||
std::string protocol, host, path, query;
|
std::string protocol, host, path, query;
|
||||||
int port;
|
int port;
|
||||||
bool websocket = true;
|
|
||||||
|
|
||||||
if (!UrlParser::parse(url, protocol, host, path, query, port, websocket))
|
if (!UrlParser::parse(url, protocol, host, path, query, port))
|
||||||
{
|
{
|
||||||
return WebSocketInitResult(false, 0,
|
return WebSocketInitResult(false, 0,
|
||||||
std::string("Could not parse URL ") + url);
|
std::string("Could not parse URL ") + url);
|
||||||
@ -164,7 +173,7 @@ namespace ix
|
|||||||
timeoutSecs);
|
timeoutSecs);
|
||||||
if (result.success)
|
if (result.success)
|
||||||
{
|
{
|
||||||
setReadyState(OPEN);
|
setReadyState(ReadyState::OPEN);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -192,22 +201,22 @@ namespace ix
|
|||||||
auto result = webSocketHandshake.serverHandshake(fd, timeoutSecs);
|
auto result = webSocketHandshake.serverHandshake(fd, timeoutSecs);
|
||||||
if (result.success)
|
if (result.success)
|
||||||
{
|
{
|
||||||
setReadyState(OPEN);
|
setReadyState(ReadyState::OPEN);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
WebSocketTransport::ReadyStateValues WebSocketTransport::getReadyState() const
|
WebSocketTransport::ReadyState WebSocketTransport::getReadyState() const
|
||||||
{
|
{
|
||||||
return _readyState;
|
return _readyState;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebSocketTransport::setReadyState(ReadyStateValues readyStateValue)
|
void WebSocketTransport::setReadyState(ReadyState readyState)
|
||||||
{
|
{
|
||||||
// No state change, return
|
// No state change, return
|
||||||
if (_readyState == readyStateValue) return;
|
if (_readyState == readyState) return;
|
||||||
|
|
||||||
if (readyStateValue == CLOSED)
|
if (readyState == ReadyState::CLOSED)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(_closeDataMutex);
|
std::lock_guard<std::mutex> lock(_closeDataMutex);
|
||||||
_onCloseCallback(_closeCode, _closeReason, _closeWireSize, _closeRemote);
|
_onCloseCallback(_closeCode, _closeReason, _closeWireSize, _closeRemote);
|
||||||
@ -217,7 +226,7 @@ namespace ix
|
|||||||
_closeRemote = false;
|
_closeRemote = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
_readyState = readyStateValue;
|
_readyState = readyState;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebSocketTransport::setOnCloseCallback(const OnCloseCallback& onCloseCallback)
|
void WebSocketTransport::setOnCloseCallback(const OnCloseCallback& onCloseCallback)
|
||||||
@ -246,11 +255,16 @@ namespace ix
|
|||||||
return now - _lastReceivePongTimePoint > std::chrono::seconds(_pingTimeoutSecs);
|
return now - _lastReceivePongTimePoint > std::chrono::seconds(_pingTimeoutSecs);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebSocketTransport::poll()
|
bool WebSocketTransport::closingDelayExceeded()
|
||||||
{
|
{
|
||||||
PollResultType pollResult = _socket->poll(_pingIntervalOrTimeoutGCDSecs);
|
std::lock_guard<std::mutex> lock(_closingTimePointMutex);
|
||||||
|
auto now = std::chrono::steady_clock::now();
|
||||||
|
return now - _closingTimePoint > std::chrono::milliseconds(kClosingMaximumWaitingDelayInMs);
|
||||||
|
}
|
||||||
|
|
||||||
if (_readyState == OPEN)
|
WebSocketTransport::PollResult WebSocketTransport::poll()
|
||||||
|
{
|
||||||
|
if (_readyState == ReadyState::OPEN)
|
||||||
{
|
{
|
||||||
// if (1) ping timeout is enabled and (2) duration since last received
|
// if (1) ping timeout is enabled and (2) duration since last received
|
||||||
// ping response (PONG) exceeds the maximum delay, then close the connection
|
// ping response (PONG) exceeds the maximum delay, then close the connection
|
||||||
@ -268,6 +282,30 @@ namespace ix
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// No timeout if state is not OPEN, otherwise computed
|
||||||
|
// pingIntervalOrTimeoutGCD (equals to -1 if no ping and no ping timeout are set)
|
||||||
|
int lastingTimeoutDelayInMs = (_readyState != ReadyState::OPEN) ? 0 : _pingIntervalOrTimeoutGCDSecs;
|
||||||
|
|
||||||
|
if (_pingIntervalOrTimeoutGCDSecs > 0)
|
||||||
|
{
|
||||||
|
// compute lasting delay to wait for next ping / timeout, if at least one set
|
||||||
|
auto now = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
|
if (now >= _nextGCDTimePoint)
|
||||||
|
{
|
||||||
|
_nextGCDTimePoint = now + std::chrono::seconds(_pingIntervalOrTimeoutGCDSecs);
|
||||||
|
|
||||||
|
lastingTimeoutDelayInMs = _pingIntervalOrTimeoutGCDSecs * 1000;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lastingTimeoutDelayInMs = (int)std::chrono::duration_cast<std::chrono::milliseconds>(_nextGCDTimePoint - now).count();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// poll the socket
|
||||||
|
PollResultType pollResult = _socket->poll(lastingTimeoutDelayInMs);
|
||||||
|
|
||||||
// Make sure we send all the buffered data
|
// Make sure we send all the buffered data
|
||||||
// there can be a lot of it for large messages.
|
// there can be a lot of it for large messages.
|
||||||
if (pollResult == PollResultType::SendRequest)
|
if (pollResult == PollResultType::SendRequest)
|
||||||
@ -281,7 +319,7 @@ namespace ix
|
|||||||
if (result == PollResultType::Error)
|
if (result == PollResultType::Error)
|
||||||
{
|
{
|
||||||
_socket->close();
|
_socket->close();
|
||||||
setReadyState(CLOSED);
|
setReadyState(ReadyState::CLOSED);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if (result == PollResultType::ReadyForWrite)
|
else if (result == PollResultType::ReadyForWrite)
|
||||||
@ -302,17 +340,12 @@ namespace ix
|
|||||||
}
|
}
|
||||||
else if (ret <= 0)
|
else if (ret <= 0)
|
||||||
{
|
{
|
||||||
_rxbuf.clear();
|
// if there are received data pending to be processed, then delay the abnormal closure
|
||||||
|
// to after dispatch (other close code/reason could be read from the buffer)
|
||||||
|
|
||||||
_socket->close();
|
_socket->close();
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(_closeDataMutex);
|
return PollResult::AbnormalClose;
|
||||||
_closeCode = kAbnormalCloseCode;
|
|
||||||
_closeReason = kAbnormalCloseMessage;
|
|
||||||
_closeWireSize = 0;
|
|
||||||
_closeRemote = true;
|
|
||||||
}
|
|
||||||
setReadyState(CLOSED);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -331,12 +364,15 @@ namespace ix
|
|||||||
_socket->close();
|
_socket->close();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Avoid a race condition where we get stuck in select
|
if (_readyState == ReadyState::CLOSING && closingDelayExceeded())
|
||||||
// while closing.
|
|
||||||
if (_readyState == CLOSING)
|
|
||||||
{
|
{
|
||||||
|
_rxbuf.clear();
|
||||||
|
// close code and reason were set when calling close()
|
||||||
_socket->close();
|
_socket->close();
|
||||||
|
setReadyState(ReadyState::CLOSED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return PollResult::Succeeded;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WebSocketTransport::isSendBufferEmpty() const
|
bool WebSocketTransport::isSendBufferEmpty() const
|
||||||
@ -398,12 +434,13 @@ namespace ix
|
|||||||
// | Payload Data continued ... |
|
// | Payload Data continued ... |
|
||||||
// +---------------------------------------------------------------+
|
// +---------------------------------------------------------------+
|
||||||
//
|
//
|
||||||
void WebSocketTransport::dispatch(const OnMessageCallback& onMessageCallback)
|
void WebSocketTransport::dispatch(WebSocketTransport::PollResult pollResult,
|
||||||
|
const OnMessageCallback& onMessageCallback)
|
||||||
{
|
{
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
wsheader_type ws;
|
wsheader_type ws;
|
||||||
if (_rxbuf.size() < 2) return; /* Need at least 2 */
|
if (_rxbuf.size() < 2) break; /* Need at least 2 */
|
||||||
const uint8_t * data = (uint8_t *) &_rxbuf[0]; // peek, but don't consume
|
const uint8_t * data = (uint8_t *) &_rxbuf[0]; // peek, but don't consume
|
||||||
ws.fin = (data[0] & 0x80) == 0x80;
|
ws.fin = (data[0] & 0x80) == 0x80;
|
||||||
ws.rsv1 = (data[0] & 0x40) == 0x40;
|
ws.rsv1 = (data[0] & 0x40) == 0x40;
|
||||||
@ -411,7 +448,7 @@ namespace ix
|
|||||||
ws.mask = (data[1] & 0x80) == 0x80;
|
ws.mask = (data[1] & 0x80) == 0x80;
|
||||||
ws.N0 = (data[1] & 0x7f);
|
ws.N0 = (data[1] & 0x7f);
|
||||||
ws.header_size = 2 + (ws.N0 == 126? 2 : 0) + (ws.N0 == 127? 8 : 0) + (ws.mask? 4 : 0);
|
ws.header_size = 2 + (ws.N0 == 126? 2 : 0) + (ws.N0 == 127? 8 : 0) + (ws.mask? 4 : 0);
|
||||||
if (_rxbuf.size() < ws.header_size) return; /* Need: ws.header_size - _rxbuf.size() */
|
if (_rxbuf.size() < ws.header_size) break; /* Need: ws.header_size - _rxbuf.size() */
|
||||||
|
|
||||||
//
|
//
|
||||||
// Calculate payload length:
|
// Calculate payload length:
|
||||||
@ -484,7 +521,7 @@ namespace ix
|
|||||||
//
|
//
|
||||||
if (ws.fin && _chunks.empty())
|
if (ws.fin && _chunks.empty())
|
||||||
{
|
{
|
||||||
emitMessage(MSG,
|
emitMessage(MessageKind::MSG,
|
||||||
std::string(_rxbuf.begin()+ws.header_size,
|
std::string(_rxbuf.begin()+ws.header_size,
|
||||||
_rxbuf.begin()+ws.header_size+(size_t) ws.N),
|
_rxbuf.begin()+ws.header_size+(size_t) ws.N),
|
||||||
ws,
|
ws,
|
||||||
@ -504,12 +541,12 @@ namespace ix
|
|||||||
_rxbuf.begin()+ws.header_size+(size_t)ws.N));
|
_rxbuf.begin()+ws.header_size+(size_t)ws.N));
|
||||||
if (ws.fin)
|
if (ws.fin)
|
||||||
{
|
{
|
||||||
emitMessage(MSG, getMergedChunks(), ws, onMessageCallback);
|
emitMessage(MessageKind::MSG, getMergedChunks(), ws, onMessageCallback);
|
||||||
_chunks.clear();
|
_chunks.clear();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
emitMessage(FRAGMENT, std::string(), ws, onMessageCallback);
|
emitMessage(MessageKind::FRAGMENT, std::string(), ws, onMessageCallback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -527,7 +564,7 @@ namespace ix
|
|||||||
sendData(wsheader_type::PONG, pingData, compress);
|
sendData(wsheader_type::PONG, pingData, compress);
|
||||||
}
|
}
|
||||||
|
|
||||||
emitMessage(PING, pingData, ws, onMessageCallback);
|
emitMessage(MessageKind::PING, pingData, ws, onMessageCallback);
|
||||||
}
|
}
|
||||||
else if (ws.opcode == wsheader_type::PONG)
|
else if (ws.opcode == wsheader_type::PONG)
|
||||||
{
|
{
|
||||||
@ -538,24 +575,62 @@ namespace ix
|
|||||||
std::lock_guard<std::mutex> lck(_lastReceivePongTimePointMutex);
|
std::lock_guard<std::mutex> lck(_lastReceivePongTimePointMutex);
|
||||||
_lastReceivePongTimePoint = std::chrono::steady_clock::now();
|
_lastReceivePongTimePoint = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
emitMessage(PONG, pongData, ws, onMessageCallback);
|
emitMessage(MessageKind::PONG, pongData, ws, onMessageCallback);
|
||||||
}
|
}
|
||||||
else if (ws.opcode == wsheader_type::CLOSE)
|
else if (ws.opcode == wsheader_type::CLOSE)
|
||||||
{
|
{
|
||||||
|
std::string reason;
|
||||||
|
uint16_t code = 0;
|
||||||
|
|
||||||
unmaskReceiveBuffer(ws);
|
unmaskReceiveBuffer(ws);
|
||||||
|
|
||||||
|
if (ws.N >= 2)
|
||||||
|
{
|
||||||
// Extract the close code first, available as the first 2 bytes
|
// Extract the close code first, available as the first 2 bytes
|
||||||
uint16_t code = 0;
|
|
||||||
code |= ((uint64_t) _rxbuf[ws.header_size]) << 8;
|
code |= ((uint64_t) _rxbuf[ws.header_size]) << 8;
|
||||||
code |= ((uint64_t) _rxbuf[ws.header_size+1]) << 0;
|
code |= ((uint64_t) _rxbuf[ws.header_size+1]) << 0;
|
||||||
|
|
||||||
// Get the reason.
|
// Get the reason.
|
||||||
std::string reason(_rxbuf.begin()+ws.header_size + 2,
|
if (ws.N > 2)
|
||||||
|
{
|
||||||
|
reason.assign(_rxbuf.begin()+ws.header_size + 2,
|
||||||
_rxbuf.begin()+ws.header_size + (size_t) ws.N);
|
_rxbuf.begin()+ws.header_size + (size_t) ws.N);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// no close code received
|
||||||
|
code = kNoStatusCodeErrorCode;
|
||||||
|
reason = kNoStatusCodeErrorMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We receive a CLOSE frame from remote and are NOT the ones who triggered the close
|
||||||
|
if (_readyState != ReadyState::CLOSING)
|
||||||
|
{
|
||||||
|
// send back the CLOSE frame
|
||||||
|
sendCloseFrame(code, reason);
|
||||||
|
|
||||||
|
_socket->wakeUpFromPoll(Socket::kCloseRequest);
|
||||||
|
|
||||||
bool remote = true;
|
bool remote = true;
|
||||||
|
closeSocketAndSwitchToClosedState(code, reason, _rxbuf.size(), remote);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// we got the CLOSE frame answer from our close, so we can close the connection if
|
||||||
|
// the code/reason are the same
|
||||||
|
bool identicalReason;
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(_closeDataMutex);
|
||||||
|
identicalReason = _closeCode == code && _closeReason == reason;
|
||||||
|
}
|
||||||
|
|
||||||
close(code, reason, _rxbuf.size(), remote);
|
if (identicalReason)
|
||||||
|
{
|
||||||
|
bool remote = false;
|
||||||
|
closeSocketAndSwitchToClosedState(code, reason, _rxbuf.size(), remote);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -568,6 +643,25 @@ namespace ix
|
|||||||
_rxbuf.erase(_rxbuf.begin(),
|
_rxbuf.erase(_rxbuf.begin(),
|
||||||
_rxbuf.begin() + ws.header_size + (size_t) ws.N);
|
_rxbuf.begin() + ws.header_size + (size_t) ws.N);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if an abnormal closure was raised in poll, and nothing else triggered a CLOSED state in
|
||||||
|
// the received and processed data then close the connection
|
||||||
|
if (pollResult == PollResult::AbnormalClose)
|
||||||
|
{
|
||||||
|
_rxbuf.clear();
|
||||||
|
|
||||||
|
// if we previously closed the connection (CLOSING state), then set state to CLOSED (code/reason were set before)
|
||||||
|
if (_readyState == ReadyState::CLOSING)
|
||||||
|
{
|
||||||
|
_socket->close();
|
||||||
|
setReadyState(ReadyState::CLOSED);
|
||||||
|
}
|
||||||
|
// if we weren't closing, then close using abnormal close code and message
|
||||||
|
else if (_readyState != ReadyState::CLOSED)
|
||||||
|
{
|
||||||
|
closeSocketAndSwitchToClosedState(kAbnormalCloseCode, kAbnormalCloseMessage, 0, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string WebSocketTransport::getMergedChunks() const
|
std::string WebSocketTransport::getMergedChunks() const
|
||||||
@ -598,7 +692,7 @@ namespace ix
|
|||||||
size_t wireSize = message.size();
|
size_t wireSize = message.size();
|
||||||
|
|
||||||
// When the RSV1 bit is 1 it means the message is compressed
|
// When the RSV1 bit is 1 it means the message is compressed
|
||||||
if (_enablePerMessageDeflate && ws.rsv1 && messageKind != FRAGMENT)
|
if (_enablePerMessageDeflate && ws.rsv1 && messageKind != MessageKind::FRAGMENT)
|
||||||
{
|
{
|
||||||
std::string decompressedMessage;
|
std::string decompressedMessage;
|
||||||
bool success = _perMessageDeflate.decompress(message, decompressedMessage);
|
bool success = _perMessageDeflate.decompress(message, decompressedMessage);
|
||||||
@ -625,7 +719,7 @@ namespace ix
|
|||||||
bool compress,
|
bool compress,
|
||||||
const OnProgressCallback& onProgressCallback)
|
const OnProgressCallback& onProgressCallback)
|
||||||
{
|
{
|
||||||
if (_readyState == CLOSING || _readyState == CLOSED)
|
if (_readyState != ReadyState::OPEN)
|
||||||
{
|
{
|
||||||
return WebSocketSendInfo();
|
return WebSocketSendInfo();
|
||||||
}
|
}
|
||||||
@ -851,7 +945,7 @@ namespace ix
|
|||||||
{
|
{
|
||||||
_socket->close();
|
_socket->close();
|
||||||
|
|
||||||
setReadyState(CLOSED);
|
setReadyState(ReadyState::CLOSED);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -861,29 +955,32 @@ namespace ix
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebSocketTransport::close(uint16_t code, const std::string& reason, size_t closeWireSize, bool remote)
|
void WebSocketTransport::sendCloseFrame(uint16_t code, const std::string& reason)
|
||||||
{
|
{
|
||||||
_requestInitCancellation = true;
|
bool compress = false;
|
||||||
|
|
||||||
if (_readyState == CLOSING || _readyState == CLOSED) return;
|
|
||||||
|
|
||||||
|
// if a status is set/was read
|
||||||
|
if (code != kNoStatusCodeErrorCode)
|
||||||
|
{
|
||||||
// See list of close events here:
|
// See list of close events here:
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent
|
// https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent
|
||||||
|
|
||||||
int codeLength = 2;
|
|
||||||
std::string closure{(char)(code >> 8), (char)(code & 0xff)};
|
std::string closure{(char)(code >> 8), (char)(code & 0xff)};
|
||||||
closure.resize(codeLength + reason.size());
|
|
||||||
|
|
||||||
// copy reason after code
|
// copy reason after code
|
||||||
closure.replace(codeLength, reason.size(), reason);
|
closure.append(reason);
|
||||||
|
|
||||||
bool compress = false;
|
|
||||||
sendData(wsheader_type::CLOSE, closure, compress);
|
sendData(wsheader_type::CLOSE, closure, compress);
|
||||||
setReadyState(CLOSING);
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// no close code/reason set
|
||||||
|
sendData(wsheader_type::CLOSE, "", compress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_socket->wakeUpFromPoll(Socket::kCloseRequest);
|
void WebSocketTransport::closeSocketAndSwitchToClosedState(uint16_t code, const std::string& reason, size_t closeWireSize, bool remote)
|
||||||
|
{
|
||||||
_socket->close();
|
_socket->close();
|
||||||
|
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(_closeDataMutex);
|
std::lock_guard<std::mutex> lock(_closeDataMutex);
|
||||||
_closeCode = code;
|
_closeCode = code;
|
||||||
@ -891,8 +988,31 @@ namespace ix
|
|||||||
_closeWireSize = closeWireSize;
|
_closeWireSize = closeWireSize;
|
||||||
_closeRemote = remote;
|
_closeRemote = remote;
|
||||||
}
|
}
|
||||||
|
setReadyState(ReadyState::CLOSED);
|
||||||
|
}
|
||||||
|
|
||||||
setReadyState(CLOSED);
|
void WebSocketTransport::close(uint16_t code, const std::string& reason, size_t closeWireSize, bool remote)
|
||||||
|
{
|
||||||
|
_requestInitCancellation = true;
|
||||||
|
|
||||||
|
if (_readyState == ReadyState::CLOSING || _readyState == ReadyState::CLOSED) return;
|
||||||
|
|
||||||
|
sendCloseFrame(code, reason);
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(_closeDataMutex);
|
||||||
|
_closeCode = code;
|
||||||
|
_closeReason = reason;
|
||||||
|
_closeWireSize = closeWireSize;
|
||||||
|
_closeRemote = remote;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(_closingTimePointMutex);
|
||||||
|
_closingTimePoint = std::chrono::steady_clock::now();
|
||||||
|
}
|
||||||
|
setReadyState(ReadyState::CLOSING);
|
||||||
|
|
||||||
|
// wake up the poll, but do not close yet
|
||||||
|
_socket->wakeUpFromPoll(Socket::kSendRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t WebSocketTransport::bufferedAmount() const
|
size_t WebSocketTransport::bufferedAmount() const
|
||||||
|
@ -40,7 +40,7 @@ namespace ix
|
|||||||
class WebSocketTransport
|
class WebSocketTransport
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
enum ReadyStateValues
|
enum class ReadyState
|
||||||
{
|
{
|
||||||
CLOSING,
|
CLOSING,
|
||||||
CLOSED,
|
CLOSED,
|
||||||
@ -48,7 +48,7 @@ namespace ix
|
|||||||
OPEN
|
OPEN
|
||||||
};
|
};
|
||||||
|
|
||||||
enum MessageKind
|
enum class MessageKind
|
||||||
{
|
{
|
||||||
MSG,
|
MSG,
|
||||||
PING,
|
PING,
|
||||||
@ -56,6 +56,12 @@ namespace ix
|
|||||||
FRAGMENT
|
FRAGMENT
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class PollResult
|
||||||
|
{
|
||||||
|
Succeeded,
|
||||||
|
AbnormalClose
|
||||||
|
};
|
||||||
|
|
||||||
using OnMessageCallback = std::function<void(const std::string&,
|
using OnMessageCallback = std::function<void(const std::string&,
|
||||||
size_t,
|
size_t,
|
||||||
bool,
|
bool,
|
||||||
@ -78,7 +84,7 @@ namespace ix
|
|||||||
WebSocketInitResult connectToSocket(int fd, // Server
|
WebSocketInitResult connectToSocket(int fd, // Server
|
||||||
int timeoutSecs);
|
int timeoutSecs);
|
||||||
|
|
||||||
void poll();
|
PollResult poll();
|
||||||
WebSocketSendInfo sendBinary(const std::string& message,
|
WebSocketSendInfo sendBinary(const std::string& message,
|
||||||
const OnProgressCallback& onProgressCallback);
|
const OnProgressCallback& onProgressCallback);
|
||||||
WebSocketSendInfo sendText(const std::string& message,
|
WebSocketSendInfo sendText(const std::string& message,
|
||||||
@ -90,10 +96,11 @@ namespace ix
|
|||||||
size_t closeWireSize = 0,
|
size_t closeWireSize = 0,
|
||||||
bool remote = false);
|
bool remote = false);
|
||||||
|
|
||||||
ReadyStateValues getReadyState() const;
|
ReadyState getReadyState() const;
|
||||||
void setReadyState(ReadyStateValues readyStateValue);
|
void setReadyState(ReadyState readyState);
|
||||||
void setOnCloseCallback(const OnCloseCallback& onCloseCallback);
|
void setOnCloseCallback(const OnCloseCallback& onCloseCallback);
|
||||||
void dispatch(const OnMessageCallback& onMessageCallback);
|
void dispatch(PollResult pollResult,
|
||||||
|
const OnMessageCallback& onMessageCallback);
|
||||||
size_t bufferedAmount() const;
|
size_t bufferedAmount() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -146,7 +153,7 @@ namespace ix
|
|||||||
std::shared_ptr<Socket> _socket;
|
std::shared_ptr<Socket> _socket;
|
||||||
|
|
||||||
// Hold the state of the connection (OPEN, CLOSED, etc...)
|
// Hold the state of the connection (OPEN, CLOSED, etc...)
|
||||||
std::atomic<ReadyStateValues> _readyState;
|
std::atomic<ReadyState> _readyState;
|
||||||
|
|
||||||
OnCloseCallback _onCloseCallback;
|
OnCloseCallback _onCloseCallback;
|
||||||
uint16_t _closeCode;
|
uint16_t _closeCode;
|
||||||
@ -163,17 +170,23 @@ namespace ix
|
|||||||
// Used to cancel dns lookup + socket connect + http upgrade
|
// Used to cancel dns lookup + socket connect + http upgrade
|
||||||
std::atomic<bool> _requestInitCancellation;
|
std::atomic<bool> _requestInitCancellation;
|
||||||
|
|
||||||
|
mutable std::mutex _closingTimePointMutex;
|
||||||
|
std::chrono::time_point<std::chrono::steady_clock>_closingTimePoint;
|
||||||
|
static const int kClosingMaximumWaitingDelayInMs;
|
||||||
|
|
||||||
// Constants for dealing with closing conneections
|
// Constants for dealing with closing conneections
|
||||||
static const uint16_t kInternalErrorCode;
|
static const uint16_t kInternalErrorCode;
|
||||||
static const uint16_t kAbnormalCloseCode;
|
static const uint16_t kAbnormalCloseCode;
|
||||||
static const uint16_t kProtocolErrorCode;
|
static const uint16_t kProtocolErrorCode;
|
||||||
|
static const uint16_t kNoStatusCodeErrorCode;
|
||||||
static const std::string kInternalErrorMessage;
|
static const std::string kInternalErrorMessage;
|
||||||
static const std::string kAbnormalCloseMessage;
|
static const std::string kAbnormalCloseMessage;
|
||||||
static const std::string kPingTimeoutMessage;
|
static const std::string kPingTimeoutMessage;
|
||||||
static const std::string kProtocolErrorMessage;
|
static const std::string kProtocolErrorMessage;
|
||||||
|
static const std::string kNoStatusCodeErrorMessage;
|
||||||
|
|
||||||
// enable auto response to ping
|
// enable auto response to ping
|
||||||
bool _enablePong;
|
std::atomic<bool> _enablePong;
|
||||||
static const bool kDefaultEnablePong;
|
static const bool kDefaultEnablePong;
|
||||||
|
|
||||||
// Optional ping and pong timeout
|
// Optional ping and pong timeout
|
||||||
@ -187,6 +200,9 @@ namespace ix
|
|||||||
static const int kDefaultPingTimeoutSecs;
|
static const int kDefaultPingTimeoutSecs;
|
||||||
static const std::string kPingMessage;
|
static const std::string kPingMessage;
|
||||||
|
|
||||||
|
// Record time step for ping/ ping timeout to ensure we wait for the right left duration
|
||||||
|
std::chrono::time_point<std::chrono::steady_clock> _nextGCDTimePoint;
|
||||||
|
|
||||||
// We record when ping are being sent so that we can know when to send the next one
|
// We record when ping are being sent so that we can know when to send the next one
|
||||||
// We also record when pong are being sent as a reply to pings, to close the connections
|
// We also record when pong are being sent as a reply to pings, to close the connections
|
||||||
// if no pong were received sufficiently fast.
|
// if no pong were received sufficiently fast.
|
||||||
@ -201,6 +217,16 @@ namespace ix
|
|||||||
// No PONG data was received through the socket for longer than ping timeout delay
|
// No PONG data was received through the socket for longer than ping timeout delay
|
||||||
bool pingTimeoutExceeded();
|
bool pingTimeoutExceeded();
|
||||||
|
|
||||||
|
// after calling close(), if no CLOSE frame answer is received back from the remote, we should close the connexion
|
||||||
|
bool closingDelayExceeded();
|
||||||
|
|
||||||
|
void sendCloseFrame(uint16_t code, const std::string& reason);
|
||||||
|
|
||||||
|
void closeSocketAndSwitchToClosedState(uint16_t code,
|
||||||
|
const std::string& reason,
|
||||||
|
size_t closeWireSize,
|
||||||
|
bool remote);
|
||||||
|
|
||||||
void sendOnSocket();
|
void sendOnSocket();
|
||||||
WebSocketSendInfo sendData(wsheader_type::opcode_type type,
|
WebSocketSendInfo sendData(wsheader_type::opcode_type type,
|
||||||
const std::string& message,
|
const std::string& message,
|
||||||
|
263
ixwebsocket/LUrlParser.cpp
Normal file
263
ixwebsocket/LUrlParser.cpp
Normal file
@ -0,0 +1,263 @@
|
|||||||
|
/*
|
||||||
|
* Lightweight URL & URI parser (RFC 1738, RFC 3986)
|
||||||
|
* https://github.com/corporateshark/LUrlParser
|
||||||
|
*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015 Sergey Kosarevsky (sk@linderdaum.com)
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "LUrlParser.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstring>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
// check if the scheme name is valid
|
||||||
|
static bool IsSchemeValid( const std::string& SchemeName )
|
||||||
|
{
|
||||||
|
for ( auto c : SchemeName )
|
||||||
|
{
|
||||||
|
if ( !isalpha( c ) && c != '+' && c != '-' && c != '.' ) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LUrlParser::clParseURL::GetPort( int* OutPort ) const
|
||||||
|
{
|
||||||
|
if ( !IsValid() ) { return false; }
|
||||||
|
|
||||||
|
int Port = atoi( m_Port.c_str() );
|
||||||
|
|
||||||
|
if ( Port <= 0 || Port > 65535 ) { return false; }
|
||||||
|
|
||||||
|
if ( OutPort ) { *OutPort = Port; }
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// based on RFC 1738 and RFC 3986
|
||||||
|
LUrlParser::clParseURL LUrlParser::clParseURL::ParseURL( const std::string& URL )
|
||||||
|
{
|
||||||
|
LUrlParser::clParseURL Result;
|
||||||
|
|
||||||
|
const char* CurrentString = URL.c_str();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* <scheme>:<scheme-specific-part>
|
||||||
|
* <scheme> := [a-z\+\-\.]+
|
||||||
|
* For resiliency, programs interpreting URLs should treat upper case letters as equivalent to lower case in scheme names
|
||||||
|
*/
|
||||||
|
|
||||||
|
// try to read scheme
|
||||||
|
{
|
||||||
|
const char* LocalString = strchr( CurrentString, ':' );
|
||||||
|
|
||||||
|
if ( !LocalString )
|
||||||
|
{
|
||||||
|
return clParseURL( LUrlParserError_NoUrlCharacter );
|
||||||
|
}
|
||||||
|
|
||||||
|
// save the scheme name
|
||||||
|
Result.m_Scheme = std::string( CurrentString, LocalString - CurrentString );
|
||||||
|
|
||||||
|
if ( !IsSchemeValid( Result.m_Scheme ) )
|
||||||
|
{
|
||||||
|
return clParseURL( LUrlParserError_InvalidSchemeName );
|
||||||
|
}
|
||||||
|
|
||||||
|
// scheme should be lowercase
|
||||||
|
std::transform( Result.m_Scheme.begin(), Result.m_Scheme.end(), Result.m_Scheme.begin(), ::tolower );
|
||||||
|
|
||||||
|
// skip ':'
|
||||||
|
CurrentString = LocalString+1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* //<user>:<password>@<host>:<port>/<url-path>
|
||||||
|
* any ":", "@" and "/" must be normalized
|
||||||
|
*/
|
||||||
|
|
||||||
|
// skip "//"
|
||||||
|
if ( *CurrentString++ != '/' ) return clParseURL( LUrlParserError_NoDoubleSlash );
|
||||||
|
if ( *CurrentString++ != '/' ) return clParseURL( LUrlParserError_NoDoubleSlash );
|
||||||
|
|
||||||
|
// check if the user name and password are specified
|
||||||
|
bool bHasUserName = false;
|
||||||
|
|
||||||
|
const char* LocalString = CurrentString;
|
||||||
|
|
||||||
|
while ( *LocalString )
|
||||||
|
{
|
||||||
|
if ( *LocalString == '@' )
|
||||||
|
{
|
||||||
|
// user name and password are specified
|
||||||
|
bHasUserName = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if ( *LocalString == '/' )
|
||||||
|
{
|
||||||
|
// end of <host>:<port> specification
|
||||||
|
bHasUserName = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalString++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// user name and password
|
||||||
|
LocalString = CurrentString;
|
||||||
|
|
||||||
|
if ( bHasUserName )
|
||||||
|
{
|
||||||
|
// read user name
|
||||||
|
while ( *LocalString && *LocalString != ':' && *LocalString != '@' ) LocalString++;
|
||||||
|
|
||||||
|
Result.m_UserName = std::string( CurrentString, LocalString - CurrentString );
|
||||||
|
|
||||||
|
// proceed with the current pointer
|
||||||
|
CurrentString = LocalString;
|
||||||
|
|
||||||
|
if ( *CurrentString == ':' )
|
||||||
|
{
|
||||||
|
// skip ':'
|
||||||
|
CurrentString++;
|
||||||
|
|
||||||
|
// read password
|
||||||
|
LocalString = CurrentString;
|
||||||
|
|
||||||
|
while ( *LocalString && *LocalString != '@' ) LocalString++;
|
||||||
|
|
||||||
|
Result.m_Password = std::string( CurrentString, LocalString - CurrentString );
|
||||||
|
|
||||||
|
CurrentString = LocalString;
|
||||||
|
}
|
||||||
|
|
||||||
|
// skip '@'
|
||||||
|
if ( *CurrentString != '@' )
|
||||||
|
{
|
||||||
|
return clParseURL( LUrlParserError_NoAtSign );
|
||||||
|
}
|
||||||
|
|
||||||
|
CurrentString++;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bHasBracket = ( *CurrentString == '[' );
|
||||||
|
|
||||||
|
// go ahead, read the host name
|
||||||
|
LocalString = CurrentString;
|
||||||
|
|
||||||
|
while ( *LocalString )
|
||||||
|
{
|
||||||
|
if ( bHasBracket && *LocalString == ']' )
|
||||||
|
{
|
||||||
|
// end of IPv6 address
|
||||||
|
LocalString++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if ( !bHasBracket && ( *LocalString == ':' || *LocalString == '/' ) )
|
||||||
|
{
|
||||||
|
// port number is specified
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalString++;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result.m_Host = std::string( CurrentString, LocalString - CurrentString );
|
||||||
|
|
||||||
|
CurrentString = LocalString;
|
||||||
|
|
||||||
|
// is port number specified?
|
||||||
|
if ( *CurrentString == ':' )
|
||||||
|
{
|
||||||
|
CurrentString++;
|
||||||
|
|
||||||
|
// read port number
|
||||||
|
LocalString = CurrentString;
|
||||||
|
|
||||||
|
while ( *LocalString && *LocalString != '/' ) LocalString++;
|
||||||
|
|
||||||
|
Result.m_Port = std::string( CurrentString, LocalString - CurrentString );
|
||||||
|
|
||||||
|
CurrentString = LocalString;
|
||||||
|
}
|
||||||
|
|
||||||
|
// end of string
|
||||||
|
if ( !*CurrentString )
|
||||||
|
{
|
||||||
|
Result.m_ErrorCode = LUrlParserError_Ok;
|
||||||
|
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// skip '/'
|
||||||
|
if ( *CurrentString != '/' )
|
||||||
|
{
|
||||||
|
return clParseURL( LUrlParserError_NoSlash );
|
||||||
|
}
|
||||||
|
|
||||||
|
CurrentString++;
|
||||||
|
|
||||||
|
// parse the path
|
||||||
|
LocalString = CurrentString;
|
||||||
|
|
||||||
|
while ( *LocalString && *LocalString != '#' && *LocalString != '?' ) LocalString++;
|
||||||
|
|
||||||
|
Result.m_Path = std::string( CurrentString, LocalString - CurrentString );
|
||||||
|
|
||||||
|
CurrentString = LocalString;
|
||||||
|
|
||||||
|
// check for query
|
||||||
|
if ( *CurrentString == '?' )
|
||||||
|
{
|
||||||
|
// skip '?'
|
||||||
|
CurrentString++;
|
||||||
|
|
||||||
|
// read query
|
||||||
|
LocalString = CurrentString;
|
||||||
|
|
||||||
|
while ( *LocalString && *LocalString != '#' ) LocalString++;
|
||||||
|
|
||||||
|
Result.m_Query = std::string( CurrentString, LocalString - CurrentString );
|
||||||
|
|
||||||
|
CurrentString = LocalString;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for fragment
|
||||||
|
if ( *CurrentString == '#' )
|
||||||
|
{
|
||||||
|
// skip '#'
|
||||||
|
CurrentString++;
|
||||||
|
|
||||||
|
// read fragment
|
||||||
|
LocalString = CurrentString;
|
||||||
|
|
||||||
|
while ( *LocalString ) LocalString++;
|
||||||
|
|
||||||
|
Result.m_Fragment = std::string( CurrentString, LocalString - CurrentString );
|
||||||
|
}
|
||||||
|
|
||||||
|
Result.m_ErrorCode = LUrlParserError_Ok;
|
||||||
|
|
||||||
|
return Result;
|
||||||
|
}
|
78
ixwebsocket/LUrlParser.h
Normal file
78
ixwebsocket/LUrlParser.h
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
* Lightweight URL & URI parser (RFC 1738, RFC 3986)
|
||||||
|
* https://github.com/corporateshark/LUrlParser
|
||||||
|
*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015 Sergey Kosarevsky (sk@linderdaum.com)
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace LUrlParser
|
||||||
|
{
|
||||||
|
enum LUrlParserError
|
||||||
|
{
|
||||||
|
LUrlParserError_Ok = 0,
|
||||||
|
LUrlParserError_Uninitialized = 1,
|
||||||
|
LUrlParserError_NoUrlCharacter = 2,
|
||||||
|
LUrlParserError_InvalidSchemeName = 3,
|
||||||
|
LUrlParserError_NoDoubleSlash = 4,
|
||||||
|
LUrlParserError_NoAtSign = 5,
|
||||||
|
LUrlParserError_UnexpectedEndOfLine = 6,
|
||||||
|
LUrlParserError_NoSlash = 7,
|
||||||
|
};
|
||||||
|
|
||||||
|
class clParseURL
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LUrlParserError m_ErrorCode;
|
||||||
|
std::string m_Scheme;
|
||||||
|
std::string m_Host;
|
||||||
|
std::string m_Port;
|
||||||
|
std::string m_Path;
|
||||||
|
std::string m_Query;
|
||||||
|
std::string m_Fragment;
|
||||||
|
std::string m_UserName;
|
||||||
|
std::string m_Password;
|
||||||
|
|
||||||
|
clParseURL()
|
||||||
|
: m_ErrorCode( LUrlParserError_Uninitialized )
|
||||||
|
{}
|
||||||
|
|
||||||
|
/// return 'true' if the parsing was successful
|
||||||
|
bool IsValid() const { return m_ErrorCode == LUrlParserError_Ok; }
|
||||||
|
|
||||||
|
/// helper to convert the port number to int, return 'true' if the port is valid (within the 0..65535 range)
|
||||||
|
bool GetPort( int* OutPort ) const;
|
||||||
|
|
||||||
|
/// parse the URL
|
||||||
|
static clParseURL ParseURL( const std::string& URL );
|
||||||
|
|
||||||
|
private:
|
||||||
|
explicit clParseURL( LUrlParserError ErrorCode )
|
||||||
|
: m_ErrorCode( ErrorCode )
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace LUrlParser
|
@ -4,13 +4,41 @@
|
|||||||
* Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
|
* Copyright (c) 2019 Machine Zone, Inc. All rights reserved.
|
||||||
*/
|
*/
|
||||||
#include "../IXSetThreadName.h"
|
#include "../IXSetThreadName.h"
|
||||||
#include <iostream>
|
#include <Windows.h>
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
|
const DWORD MS_VC_EXCEPTION = 0x406D1388;
|
||||||
|
|
||||||
|
#pragma pack(push,8)
|
||||||
|
typedef struct tagTHREADNAME_INFO
|
||||||
|
{
|
||||||
|
DWORD dwType; // Must be 0x1000.
|
||||||
|
LPCSTR szName; // Pointer to name (in user addr space).
|
||||||
|
DWORD dwThreadID; // Thread ID (-1=caller thread).
|
||||||
|
DWORD dwFlags; // Reserved for future use, must be zero.
|
||||||
|
} THREADNAME_INFO;
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
void SetThreadName(DWORD dwThreadID, const char* threadName)
|
||||||
|
{
|
||||||
|
THREADNAME_INFO info;
|
||||||
|
info.dwType = 0x1000;
|
||||||
|
info.szName = threadName;
|
||||||
|
info.dwThreadID = dwThreadID;
|
||||||
|
info.dwFlags = 0;
|
||||||
|
|
||||||
|
__try
|
||||||
|
{
|
||||||
|
RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)& info);
|
||||||
|
}
|
||||||
|
__except (EXCEPTION_EXECUTE_HANDLER)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void setThreadName(const std::string& name)
|
void setThreadName(const std::string& name)
|
||||||
{
|
{
|
||||||
// FIXME
|
SetThreadName(-1, name.c_str());
|
||||||
std::cerr << "setThreadName not implemented on Windows yet" << std::endl;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,8 @@ add_subdirectory(${PROJECT_SOURCE_DIR}/.. ixwebsocket)
|
|||||||
include_directories(
|
include_directories(
|
||||||
${PROJECT_SOURCE_DIR}/Catch2/single_include
|
${PROJECT_SOURCE_DIR}/Catch2/single_include
|
||||||
../third_party/msgpack11
|
../third_party/msgpack11
|
||||||
|
../third_party/spdlog/include
|
||||||
|
../ws
|
||||||
)
|
)
|
||||||
|
|
||||||
# Shared sources
|
# Shared sources
|
||||||
@ -28,6 +30,7 @@ set (SOURCES
|
|||||||
test_runner.cpp
|
test_runner.cpp
|
||||||
IXTest.cpp
|
IXTest.cpp
|
||||||
../third_party/msgpack11/msgpack11.cpp
|
../third_party/msgpack11/msgpack11.cpp
|
||||||
|
../ws/ixcore/utils/IXCoreLogger.cpp
|
||||||
|
|
||||||
IXDNSLookupTest.cpp
|
IXDNSLookupTest.cpp
|
||||||
IXSocketTest.cpp
|
IXSocketTest.cpp
|
||||||
@ -35,6 +38,7 @@ set (SOURCES
|
|||||||
IXWebSocketServerTest.cpp
|
IXWebSocketServerTest.cpp
|
||||||
IXWebSocketPingTest.cpp
|
IXWebSocketPingTest.cpp
|
||||||
IXWebSocketTestConnectionDisconnection.cpp
|
IXWebSocketTestConnectionDisconnection.cpp
|
||||||
|
IXUrlParserTest.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
# Some unittest don't work on windows yet
|
# Some unittest don't work on windows yet
|
||||||
|
@ -27,15 +27,6 @@ namespace ix
|
|||||||
struct Logger
|
struct Logger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Logger& operator<<(const std::string& msg)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(_mutex);
|
|
||||||
|
|
||||||
std::cerr << msg;
|
|
||||||
std::cerr << std::endl;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
Logger& operator<<(T const& obj)
|
Logger& operator<<(T const& obj)
|
||||||
{
|
{
|
||||||
|
108
test/IXUrlParserTest.cpp
Normal file
108
test/IXUrlParserTest.cpp
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
/*
|
||||||
|
* IXSocketTest.cpp
|
||||||
|
* Author: Korchynskyi Dmytro
|
||||||
|
* Copyright (c) 2019 Machine Zone. All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <ixwebsocket/IXUrlParser.h>
|
||||||
|
|
||||||
|
#include "IXTest.h"
|
||||||
|
#include "catch.hpp"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
using namespace ix;
|
||||||
|
|
||||||
|
namespace ix
|
||||||
|
{
|
||||||
|
|
||||||
|
TEST_CASE("urlParser", "[urlParser]")
|
||||||
|
{
|
||||||
|
SECTION("http://google.com")
|
||||||
|
{
|
||||||
|
std::string url = "http://google.com";
|
||||||
|
std::string protocol, host, path, query;
|
||||||
|
int port;
|
||||||
|
bool res;
|
||||||
|
|
||||||
|
res = UrlParser::parse(url, protocol, host, path, query, port);
|
||||||
|
|
||||||
|
REQUIRE(res);
|
||||||
|
REQUIRE(protocol == "http");
|
||||||
|
REQUIRE(host == "google.com");
|
||||||
|
REQUIRE(path == "/");
|
||||||
|
REQUIRE(query == "");
|
||||||
|
REQUIRE(port == 80); // default port for http
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("https://google.com")
|
||||||
|
{
|
||||||
|
std::string url = "https://google.com";
|
||||||
|
std::string protocol, host, path, query;
|
||||||
|
int port;
|
||||||
|
bool res;
|
||||||
|
|
||||||
|
res = UrlParser::parse(url, protocol, host, path, query, port);
|
||||||
|
|
||||||
|
REQUIRE(res);
|
||||||
|
REQUIRE(protocol == "https");
|
||||||
|
REQUIRE(host == "google.com");
|
||||||
|
REQUIRE(path == "/");
|
||||||
|
REQUIRE(query == "");
|
||||||
|
REQUIRE(port == 443); // default port for https
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("ws://google.com")
|
||||||
|
{
|
||||||
|
std::string url = "ws://google.com";
|
||||||
|
std::string protocol, host, path, query;
|
||||||
|
int port;
|
||||||
|
bool res;
|
||||||
|
|
||||||
|
res = UrlParser::parse(url, protocol, host, path, query, port);
|
||||||
|
|
||||||
|
REQUIRE(res);
|
||||||
|
REQUIRE(protocol == "ws");
|
||||||
|
REQUIRE(host == "google.com");
|
||||||
|
REQUIRE(path == "/");
|
||||||
|
REQUIRE(query == "");
|
||||||
|
REQUIRE(port == 80); // default port for ws
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("wss://google.com/ws?arg=value")
|
||||||
|
{
|
||||||
|
std::string url = "wss://google.com/ws?arg=value&arg2=value2";
|
||||||
|
std::string protocol, host, path, query;
|
||||||
|
int port;
|
||||||
|
bool res;
|
||||||
|
|
||||||
|
res = UrlParser::parse(url, protocol, host, path, query, port);
|
||||||
|
|
||||||
|
REQUIRE(res);
|
||||||
|
REQUIRE(protocol == "wss");
|
||||||
|
REQUIRE(host == "google.com");
|
||||||
|
REQUIRE(path == "/ws?arg=value&arg2=value2");
|
||||||
|
REQUIRE(query == "arg=value&arg2=value2");
|
||||||
|
REQUIRE(port == 443); // default port for wss
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("real test")
|
||||||
|
{
|
||||||
|
std::string url = "ws://127.0.0.1:7350/ws?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NTcxNzAwNzIsInVpZCI6ImMwZmZjOGE1LTk4OTktNDAwYi1hNGU5LTJjNWM3NjFmNWQxZiIsInVzbiI6InN2YmhOdlNJSmEifQ.5L8BUbpTA4XAHlSrdwhIVlrlIpRtjExepim7Yh5eEO4&status=true&format=protobuf";
|
||||||
|
std::string protocol, host, path, query;
|
||||||
|
int port;
|
||||||
|
bool res;
|
||||||
|
|
||||||
|
res = UrlParser::parse(url, protocol, host, path, query, port);
|
||||||
|
|
||||||
|
REQUIRE(res);
|
||||||
|
REQUIRE(protocol == "ws");
|
||||||
|
REQUIRE(host == "127.0.0.1");
|
||||||
|
REQUIRE(path == "/ws?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NTcxNzAwNzIsInVpZCI6ImMwZmZjOGE1LTk4OTktNDAwYi1hNGU5LTJjNWM3NjFmNWQxZiIsInVzbiI6InN2YmhOdlNJSmEifQ.5L8BUbpTA4XAHlSrdwhIVlrlIpRtjExepim7Yh5eEO4&status=true&format=protobuf");
|
||||||
|
REQUIRE(query == "token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NTcxNzAwNzIsInVpZCI6ImMwZmZjOGE1LTk4OTktNDAwYi1hNGU5LTJjNWM3NjFmNWQxZiIsInVzbiI6InN2YmhOdlNJSmEifQ.5L8BUbpTA4XAHlSrdwhIVlrlIpRtjExepim7Yh5eEO4&status=true&format=protobuf");
|
||||||
|
REQUIRE(port == 7350);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -43,7 +43,7 @@ namespace
|
|||||||
|
|
||||||
bool WebSocketClient::isReady() const
|
bool WebSocketClient::isReady() const
|
||||||
{
|
{
|
||||||
return _webSocket.getReadyState() == ix::WebSocket_ReadyState_Open;
|
return _webSocket.getReadyState() == ix::ReadyState::Open;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebSocketClient::stop()
|
void WebSocketClient::stop()
|
||||||
@ -68,9 +68,13 @@ namespace
|
|||||||
// The important bit for this test.
|
// The important bit for this test.
|
||||||
// Set a 1 second heartbeat with the setter method to test
|
// Set a 1 second heartbeat with the setter method to test
|
||||||
if (_useHeartBeatMethod)
|
if (_useHeartBeatMethod)
|
||||||
|
{
|
||||||
_webSocket.setHeartBeatPeriod(1);
|
_webSocket.setHeartBeatPeriod(1);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
_webSocket.setPingInterval(1);
|
_webSocket.setPingInterval(1);
|
||||||
|
}
|
||||||
|
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
log(std::string("Connecting to url: ") + url);
|
log(std::string("Connecting to url: ") + url);
|
||||||
@ -84,33 +88,32 @@ namespace
|
|||||||
const ix::WebSocketCloseInfo& closeInfo)
|
const ix::WebSocketCloseInfo& closeInfo)
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
if (messageType == ix::WebSocket_MessageType_Open)
|
if (messageType == ix::WebSocketMessageType::Open)
|
||||||
{
|
{
|
||||||
log("client connected");
|
log("client connected");
|
||||||
}
|
}
|
||||||
else if (messageType == ix::WebSocket_MessageType_Close)
|
else if (messageType == ix::WebSocketMessageType::Close)
|
||||||
{
|
{
|
||||||
log("client disconnected");
|
log("client disconnected");
|
||||||
}
|
}
|
||||||
else if (messageType == ix::WebSocket_MessageType_Error)
|
else if (messageType == ix::WebSocketMessageType::Error)
|
||||||
{
|
{
|
||||||
ss << "Error ! " << error.reason;
|
ss << "Error ! " << error.reason;
|
||||||
log(ss.str());
|
log(ss.str());
|
||||||
}
|
}
|
||||||
else if (messageType == ix::WebSocket_MessageType_Pong)
|
else if (messageType == ix::WebSocketMessageType::Pong)
|
||||||
{
|
{
|
||||||
ss << "Received pong message " << str;
|
ss << "Received pong message " << str;
|
||||||
log(ss.str());
|
log(ss.str());
|
||||||
}
|
}
|
||||||
else if (messageType == ix::WebSocket_MessageType_Ping)
|
else if (messageType == ix::WebSocketMessageType::Ping)
|
||||||
{
|
{
|
||||||
ss << "Received ping message " << str;
|
ss << "Received ping message " << str;
|
||||||
log(ss.str());
|
log(ss.str());
|
||||||
}
|
}
|
||||||
else if (messageType == ix::WebSocket_MessageType_Message)
|
else if (messageType == ix::WebSocketMessageType::Message)
|
||||||
{
|
{
|
||||||
ss << "Received message " << str;
|
// too many messages to log
|
||||||
log(ss.str());
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -142,7 +145,7 @@ namespace
|
|||||||
const ix::WebSocketOpenInfo& openInfo,
|
const ix::WebSocketOpenInfo& openInfo,
|
||||||
const ix::WebSocketCloseInfo& closeInfo)
|
const ix::WebSocketCloseInfo& closeInfo)
|
||||||
{
|
{
|
||||||
if (messageType == ix::WebSocket_MessageType_Open)
|
if (messageType == ix::WebSocketMessageType::Open)
|
||||||
{
|
{
|
||||||
Logger() << "New server connection";
|
Logger() << "New server connection";
|
||||||
Logger() << "id: " << connectionState->getId();
|
Logger() << "id: " << connectionState->getId();
|
||||||
@ -153,15 +156,23 @@ namespace
|
|||||||
Logger() << it.first << ": " << it.second;
|
Logger() << it.first << ": " << it.second;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (messageType == ix::WebSocket_MessageType_Close)
|
else if (messageType == ix::WebSocketMessageType::Close)
|
||||||
{
|
{
|
||||||
log("Server closed connection");
|
log("Server closed connection");
|
||||||
}
|
}
|
||||||
else if (messageType == ix::WebSocket_MessageType_Ping)
|
else if (messageType == ix::WebSocketMessageType::Ping)
|
||||||
{
|
{
|
||||||
log("Server received a ping");
|
log("Server received a ping");
|
||||||
receivedPingMessages++;
|
receivedPingMessages++;
|
||||||
}
|
}
|
||||||
|
else if (messageType == ix::WebSocketMessageType::Message)
|
||||||
|
{
|
||||||
|
// to many messages to log
|
||||||
|
for(auto client: server.getClients())
|
||||||
|
{
|
||||||
|
client->sendText("reply");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -179,96 +190,6 @@ namespace
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Websocket_ping_no_data_sent_setHeartBeatPeriod", "[setHeartBeatPeriod]")
|
|
||||||
{
|
|
||||||
SECTION("Make sure that ping messages are sent when no other data are sent.")
|
|
||||||
{
|
|
||||||
ix::setupWebSocketTrafficTrackerCallback();
|
|
||||||
|
|
||||||
int port = getFreePort();
|
|
||||||
ix::WebSocketServer server(port);
|
|
||||||
std::atomic<int> serverReceivedPingMessages(0);
|
|
||||||
REQUIRE(startServer(server, serverReceivedPingMessages));
|
|
||||||
|
|
||||||
std::string session = ix::generateSessionId();
|
|
||||||
bool useSetHeartBeatPeriodMethod = true;
|
|
||||||
WebSocketClient webSocketClient(port, useSetHeartBeatPeriodMethod);
|
|
||||||
|
|
||||||
webSocketClient.start();
|
|
||||||
|
|
||||||
// Wait for all chat instance to be ready
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
if (webSocketClient.isReady()) break;
|
|
||||||
ix::msleep(10);
|
|
||||||
}
|
|
||||||
|
|
||||||
REQUIRE(server.getClients().size() == 1);
|
|
||||||
|
|
||||||
ix::msleep(1900);
|
|
||||||
|
|
||||||
webSocketClient.stop();
|
|
||||||
|
|
||||||
|
|
||||||
// Here we test ping interval
|
|
||||||
// -> expected ping messages == 1 as 1900 seconds, 1 ping sent every second
|
|
||||||
REQUIRE(serverReceivedPingMessages == 1);
|
|
||||||
|
|
||||||
// Give us 500ms for the server to notice that clients went away
|
|
||||||
ix::msleep(500);
|
|
||||||
REQUIRE(server.getClients().size() == 0);
|
|
||||||
|
|
||||||
ix::reportWebSocketTraffic();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("Websocket_ping_data_sent_setHeartBeatPeriod", "[setHeartBeatPeriod]")
|
|
||||||
{
|
|
||||||
SECTION("Make sure that ping messages are sent, even if other messages are sent")
|
|
||||||
{
|
|
||||||
ix::setupWebSocketTrafficTrackerCallback();
|
|
||||||
|
|
||||||
int port = getFreePort();
|
|
||||||
ix::WebSocketServer server(port);
|
|
||||||
std::atomic<int> serverReceivedPingMessages(0);
|
|
||||||
REQUIRE(startServer(server, serverReceivedPingMessages));
|
|
||||||
|
|
||||||
std::string session = ix::generateSessionId();
|
|
||||||
bool useSetHeartBeatPeriodMethod = true;
|
|
||||||
WebSocketClient webSocketClient(port, useSetHeartBeatPeriodMethod);
|
|
||||||
|
|
||||||
webSocketClient.start();
|
|
||||||
|
|
||||||
// Wait for all chat instance to be ready
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
if (webSocketClient.isReady()) break;
|
|
||||||
ix::msleep(10);
|
|
||||||
}
|
|
||||||
|
|
||||||
REQUIRE(server.getClients().size() == 1);
|
|
||||||
|
|
||||||
ix::msleep(900);
|
|
||||||
webSocketClient.sendMessage("hello world");
|
|
||||||
ix::msleep(900);
|
|
||||||
webSocketClient.sendMessage("hello world");
|
|
||||||
ix::msleep(1100);
|
|
||||||
|
|
||||||
webSocketClient.stop();
|
|
||||||
|
|
||||||
// Here we test ping interval
|
|
||||||
// client has sent data, but ping should have been sent no matter what
|
|
||||||
// -> expected ping messages == 2 as 900+900+1100 = 2900 seconds, 1 ping sent every second
|
|
||||||
REQUIRE(serverReceivedPingMessages == 2);
|
|
||||||
|
|
||||||
// Give us 500ms for the server to notice that clients went away
|
|
||||||
ix::msleep(500);
|
|
||||||
REQUIRE(server.getClients().size() == 0);
|
|
||||||
|
|
||||||
ix::reportWebSocketTraffic();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("Websocket_ping_no_data_sent_setPingInterval", "[setPingInterval]")
|
TEST_CASE("Websocket_ping_no_data_sent_setPingInterval", "[setPingInterval]")
|
||||||
{
|
{
|
||||||
SECTION("Make sure that ping messages are sent when no other data are sent.")
|
SECTION("Make sure that ping messages are sent when no other data are sent.")
|
||||||
@ -346,9 +267,6 @@ TEST_CASE("Websocket_ping_data_sent_setPingInterval", "[setPingInterval]")
|
|||||||
|
|
||||||
webSocketClient.stop();
|
webSocketClient.stop();
|
||||||
|
|
||||||
// without this sleep test fails on Windows
|
|
||||||
ix::msleep(100);
|
|
||||||
|
|
||||||
// Here we test ping interval
|
// Here we test ping interval
|
||||||
// client has sent data, but ping should have been sent no matter what
|
// client has sent data, but ping should have been sent no matter what
|
||||||
// -> expected ping messages == 3 as 900+900+1300 = 3100 seconds, 1 ping sent every second
|
// -> expected ping messages == 3 as 900+900+1300 = 3100 seconds, 1 ping sent every second
|
||||||
@ -361,3 +279,203 @@ TEST_CASE("Websocket_ping_data_sent_setPingInterval", "[setPingInterval]")
|
|||||||
ix::reportWebSocketTraffic();
|
ix::reportWebSocketTraffic();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Websocket_ping_data_sent_setPingInterval_half_full", "[setPingInterval]")
|
||||||
|
{
|
||||||
|
SECTION("Make sure that ping messages are sent, even if other messages are sent continuously during a given time")
|
||||||
|
{
|
||||||
|
ix::setupWebSocketTrafficTrackerCallback();
|
||||||
|
|
||||||
|
int port = getFreePort();
|
||||||
|
ix::WebSocketServer server(port);
|
||||||
|
std::atomic<int> serverReceivedPingMessages(0);
|
||||||
|
REQUIRE(startServer(server, serverReceivedPingMessages));
|
||||||
|
|
||||||
|
std::string session = ix::generateSessionId();
|
||||||
|
bool useSetHeartBeatPeriodMethod = false; // so use setPingInterval
|
||||||
|
WebSocketClient webSocketClient(port, useSetHeartBeatPeriodMethod);
|
||||||
|
|
||||||
|
webSocketClient.start();
|
||||||
|
|
||||||
|
// Wait for all chat instance to be ready
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (webSocketClient.isReady()) break;
|
||||||
|
ix::msleep(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
REQUIRE(server.getClients().size() == 1);
|
||||||
|
|
||||||
|
// send continuously for 1100ms
|
||||||
|
auto now = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
|
while(std::chrono::steady_clock::now() - now <= std::chrono::milliseconds(900))
|
||||||
|
{
|
||||||
|
webSocketClient.sendMessage("message");
|
||||||
|
ix::msleep(1);
|
||||||
|
}
|
||||||
|
ix::msleep(150);
|
||||||
|
|
||||||
|
// Here we test ping interval
|
||||||
|
// client has sent data, but ping should have been sent no matter what
|
||||||
|
// -> expected ping messages == 1, as 900+150 = 1050ms, 1 ping sent every second
|
||||||
|
REQUIRE(serverReceivedPingMessages == 1);
|
||||||
|
|
||||||
|
ix::msleep(100);
|
||||||
|
|
||||||
|
webSocketClient.stop();
|
||||||
|
|
||||||
|
// Give us 500ms for the server to notice that clients went away
|
||||||
|
ix::msleep(500);
|
||||||
|
REQUIRE(server.getClients().size() == 0);
|
||||||
|
|
||||||
|
ix::reportWebSocketTraffic();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Websocket_ping_data_sent_setPingInterval_full", "[setPingInterval]")
|
||||||
|
{
|
||||||
|
SECTION("Make sure that ping messages are sent, even if other messages are sent continuously for longer than ping interval")
|
||||||
|
{
|
||||||
|
ix::setupWebSocketTrafficTrackerCallback();
|
||||||
|
|
||||||
|
int port = getFreePort();
|
||||||
|
ix::WebSocketServer server(port);
|
||||||
|
std::atomic<int> serverReceivedPingMessages(0);
|
||||||
|
REQUIRE(startServer(server, serverReceivedPingMessages));
|
||||||
|
|
||||||
|
std::string session = ix::generateSessionId();
|
||||||
|
bool useSetHeartBeatPeriodMethod = false; // so use setPingInterval
|
||||||
|
WebSocketClient webSocketClient(port, useSetHeartBeatPeriodMethod);
|
||||||
|
|
||||||
|
webSocketClient.start();
|
||||||
|
|
||||||
|
// Wait for all chat instance to be ready
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (webSocketClient.isReady()) break;
|
||||||
|
ix::msleep(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
REQUIRE(server.getClients().size() == 1);
|
||||||
|
|
||||||
|
// send continuously for 1100ms
|
||||||
|
auto now = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
|
while(std::chrono::steady_clock::now() - now <= std::chrono::milliseconds(1100))
|
||||||
|
{
|
||||||
|
webSocketClient.sendMessage("message");
|
||||||
|
ix::msleep(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Here we test ping interval
|
||||||
|
// client has sent data, but ping should have been sent no matter what
|
||||||
|
// -> expected ping messages == 1, 1 ping sent every second
|
||||||
|
REQUIRE(serverReceivedPingMessages == 1);
|
||||||
|
|
||||||
|
ix::msleep(100);
|
||||||
|
|
||||||
|
webSocketClient.stop();
|
||||||
|
|
||||||
|
// Give us 500ms for the server to notice that clients went away
|
||||||
|
ix::msleep(500);
|
||||||
|
REQUIRE(server.getClients().size() == 0);
|
||||||
|
|
||||||
|
ix::reportWebSocketTraffic();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Using setHeartBeatPeriod
|
||||||
|
|
||||||
|
TEST_CASE("Websocket_ping_no_data_sent_setHeartBeatPeriod", "[setHeartBeatPeriod]")
|
||||||
|
{
|
||||||
|
SECTION("Make sure that ping messages are sent when no other data are sent.")
|
||||||
|
{
|
||||||
|
ix::setupWebSocketTrafficTrackerCallback();
|
||||||
|
|
||||||
|
int port = getFreePort();
|
||||||
|
ix::WebSocketServer server(port);
|
||||||
|
std::atomic<int> serverReceivedPingMessages(0);
|
||||||
|
REQUIRE(startServer(server, serverReceivedPingMessages));
|
||||||
|
|
||||||
|
std::string session = ix::generateSessionId();
|
||||||
|
bool useSetHeartBeatPeriodMethod = true;
|
||||||
|
WebSocketClient webSocketClient(port, useSetHeartBeatPeriodMethod);
|
||||||
|
|
||||||
|
webSocketClient.start();
|
||||||
|
|
||||||
|
// Wait for all chat instance to be ready
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (webSocketClient.isReady()) break;
|
||||||
|
ix::msleep(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
REQUIRE(server.getClients().size() == 1);
|
||||||
|
|
||||||
|
ix::msleep(1850);
|
||||||
|
|
||||||
|
webSocketClient.stop();
|
||||||
|
|
||||||
|
|
||||||
|
// Here we test ping interval
|
||||||
|
// -> expected ping messages == 1 as 1850 seconds, 1 ping sent every second
|
||||||
|
REQUIRE(serverReceivedPingMessages == 1);
|
||||||
|
|
||||||
|
// Give us 500ms for the server to notice that clients went away
|
||||||
|
ix::msleep(500);
|
||||||
|
REQUIRE(server.getClients().size() == 0);
|
||||||
|
|
||||||
|
ix::reportWebSocketTraffic();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Websocket_ping_data_sent_setHeartBeatPeriod", "[setHeartBeatPeriod]")
|
||||||
|
{
|
||||||
|
SECTION("Make sure that ping messages are sent, even if other messages are sent")
|
||||||
|
{
|
||||||
|
ix::setupWebSocketTrafficTrackerCallback();
|
||||||
|
|
||||||
|
int port = getFreePort();
|
||||||
|
ix::WebSocketServer server(port);
|
||||||
|
std::atomic<int> serverReceivedPingMessages(0);
|
||||||
|
REQUIRE(startServer(server, serverReceivedPingMessages));
|
||||||
|
|
||||||
|
std::string session = ix::generateSessionId();
|
||||||
|
bool useSetHeartBeatPeriodMethod = true;
|
||||||
|
WebSocketClient webSocketClient(port, useSetHeartBeatPeriodMethod);
|
||||||
|
|
||||||
|
webSocketClient.start();
|
||||||
|
|
||||||
|
// Wait for all chat instance to be ready
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (webSocketClient.isReady()) break;
|
||||||
|
ix::msleep(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
REQUIRE(server.getClients().size() == 1);
|
||||||
|
|
||||||
|
ix::msleep(900);
|
||||||
|
webSocketClient.sendMessage("hello world");
|
||||||
|
ix::msleep(900);
|
||||||
|
webSocketClient.sendMessage("hello world");
|
||||||
|
ix::msleep(900);
|
||||||
|
|
||||||
|
webSocketClient.stop();
|
||||||
|
|
||||||
|
// without this sleep test fails on Windows
|
||||||
|
ix::msleep(100);
|
||||||
|
|
||||||
|
// Here we test ping interval
|
||||||
|
// client has sent data, but ping should have been sent no matter what
|
||||||
|
// -> expected ping messages == 2 as 900+900+900 = 2700 seconds, 1 ping sent every second
|
||||||
|
REQUIRE(serverReceivedPingMessages == 2);
|
||||||
|
|
||||||
|
// Give us 500ms for the server to notice that clients went away
|
||||||
|
ix::msleep(500);
|
||||||
|
REQUIRE(server.getClients().size() == 0);
|
||||||
|
|
||||||
|
ix::reportWebSocketTraffic();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -52,12 +52,12 @@ namespace
|
|||||||
|
|
||||||
bool WebSocketClient::isReady() const
|
bool WebSocketClient::isReady() const
|
||||||
{
|
{
|
||||||
return _webSocket.getReadyState() == ix::WebSocket_ReadyState_Open;
|
return _webSocket.getReadyState() == ix::ReadyState::Open;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WebSocketClient::isClosed() const
|
bool WebSocketClient::isClosed() const
|
||||||
{
|
{
|
||||||
return _webSocket.getReadyState() == ix::WebSocket_ReadyState_Closed;
|
return _webSocket.getReadyState() == ix::ReadyState::Closed;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebSocketClient::stop()
|
void WebSocketClient::stop()
|
||||||
@ -78,6 +78,7 @@ namespace
|
|||||||
}
|
}
|
||||||
|
|
||||||
_webSocket.setUrl(url);
|
_webSocket.setUrl(url);
|
||||||
|
_webSocket.disableAutomaticReconnection();
|
||||||
|
|
||||||
// The important bit for this test.
|
// The important bit for this test.
|
||||||
// Set a ping interval, and a ping timeout
|
// Set a ping interval, and a ping timeout
|
||||||
@ -96,13 +97,12 @@ namespace
|
|||||||
const ix::WebSocketCloseInfo& closeInfo)
|
const ix::WebSocketCloseInfo& closeInfo)
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
if (messageType == ix::WebSocket_MessageType_Open)
|
if (messageType == ix::WebSocketMessageType::Open)
|
||||||
{
|
{
|
||||||
log("client connected");
|
log("client connected");
|
||||||
|
|
||||||
_webSocket.disableAutomaticReconnection();
|
|
||||||
}
|
}
|
||||||
else if (messageType == ix::WebSocket_MessageType_Close)
|
else if (messageType == ix::WebSocketMessageType::Close)
|
||||||
{
|
{
|
||||||
log("client disconnected");
|
log("client disconnected");
|
||||||
|
|
||||||
@ -111,29 +111,25 @@ namespace
|
|||||||
_closedDueToPingTimeout = true;
|
_closedDueToPingTimeout = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
_webSocket.disableAutomaticReconnection();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
else if (messageType == ix::WebSocket_MessageType_Error)
|
else if (messageType == ix::WebSocketMessageType::Error)
|
||||||
{
|
{
|
||||||
ss << "Error ! " << error.reason;
|
ss << "Error ! " << error.reason;
|
||||||
log(ss.str());
|
log(ss.str());
|
||||||
|
|
||||||
_webSocket.disableAutomaticReconnection();
|
|
||||||
}
|
}
|
||||||
else if (messageType == ix::WebSocket_MessageType_Pong)
|
else if (messageType == ix::WebSocketMessageType::Pong)
|
||||||
{
|
{
|
||||||
_receivedPongMessages++;
|
_receivedPongMessages++;
|
||||||
|
|
||||||
ss << "Received pong message " << str;
|
ss << "Received pong message " << str;
|
||||||
log(ss.str());
|
log(ss.str());
|
||||||
}
|
}
|
||||||
else if (messageType == ix::WebSocket_MessageType_Ping)
|
else if (messageType == ix::WebSocketMessageType::Ping)
|
||||||
{
|
{
|
||||||
ss << "Received ping message " << str;
|
ss << "Received ping message " << str;
|
||||||
log(ss.str());
|
log(ss.str());
|
||||||
}
|
}
|
||||||
else if (messageType == ix::WebSocket_MessageType_Message)
|
else if (messageType == ix::WebSocketMessageType::Message)
|
||||||
{
|
{
|
||||||
ss << "Received message " << str;
|
ss << "Received message " << str;
|
||||||
log(ss.str());
|
log(ss.str());
|
||||||
@ -178,7 +174,7 @@ namespace
|
|||||||
const ix::WebSocketOpenInfo& openInfo,
|
const ix::WebSocketOpenInfo& openInfo,
|
||||||
const ix::WebSocketCloseInfo& closeInfo)
|
const ix::WebSocketCloseInfo& closeInfo)
|
||||||
{
|
{
|
||||||
if (messageType == ix::WebSocket_MessageType_Open)
|
if (messageType == ix::WebSocketMessageType::Open)
|
||||||
{
|
{
|
||||||
Logger() << "New server connection";
|
Logger() << "New server connection";
|
||||||
Logger() << "id: " << connectionState->getId();
|
Logger() << "id: " << connectionState->getId();
|
||||||
@ -189,11 +185,11 @@ namespace
|
|||||||
Logger() << it.first << ": " << it.second;
|
Logger() << it.first << ": " << it.second;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (messageType == ix::WebSocket_MessageType_Close)
|
else if (messageType == ix::WebSocketMessageType::Close)
|
||||||
{
|
{
|
||||||
log("Server closed connection");
|
log("Server closed connection");
|
||||||
}
|
}
|
||||||
else if (messageType == ix::WebSocket_MessageType_Ping)
|
else if (messageType == ix::WebSocketMessageType::Ping)
|
||||||
{
|
{
|
||||||
log("Server received a ping");
|
log("Server received a ping");
|
||||||
receivedPingMessages++;
|
receivedPingMessages++;
|
||||||
@ -354,7 +350,7 @@ TEST_CASE("Websocket_no_ping_but_timeout", "[setPingTimeout]")
|
|||||||
|
|
||||||
REQUIRE(server.getClients().size() == 1);
|
REQUIRE(server.getClients().size() == 1);
|
||||||
|
|
||||||
ix::msleep(2900);
|
ix::msleep(2700);
|
||||||
|
|
||||||
// Here we test ping timeout, no timeout yet
|
// Here we test ping timeout, no timeout yet
|
||||||
REQUIRE(serverReceivedPingMessages == 0);
|
REQUIRE(serverReceivedPingMessages == 0);
|
||||||
@ -363,7 +359,7 @@ TEST_CASE("Websocket_no_ping_but_timeout", "[setPingTimeout]")
|
|||||||
REQUIRE(webSocketClient.isClosed() == false);
|
REQUIRE(webSocketClient.isClosed() == false);
|
||||||
REQUIRE(webSocketClient.closedDueToPingTimeout() == false);
|
REQUIRE(webSocketClient.closedDueToPingTimeout() == false);
|
||||||
|
|
||||||
ix::msleep(200);
|
ix::msleep(400);
|
||||||
|
|
||||||
// Here we test ping timeout, timeout
|
// Here we test ping timeout, timeout
|
||||||
REQUIRE(serverReceivedPingMessages == 0);
|
REQUIRE(serverReceivedPingMessages == 0);
|
||||||
|
@ -50,7 +50,7 @@ namespace ix
|
|||||||
const ix::WebSocketOpenInfo& openInfo,
|
const ix::WebSocketOpenInfo& openInfo,
|
||||||
const ix::WebSocketCloseInfo& closeInfo)
|
const ix::WebSocketCloseInfo& closeInfo)
|
||||||
{
|
{
|
||||||
if (messageType == ix::WebSocket_MessageType_Open)
|
if (messageType == ix::WebSocketMessageType::Open)
|
||||||
{
|
{
|
||||||
Logger() << "New connection";
|
Logger() << "New connection";
|
||||||
connectionState->computeId();
|
connectionState->computeId();
|
||||||
@ -64,11 +64,11 @@ namespace ix
|
|||||||
|
|
||||||
connectionId = connectionState->getId();
|
connectionId = connectionState->getId();
|
||||||
}
|
}
|
||||||
else if (messageType == ix::WebSocket_MessageType_Close)
|
else if (messageType == ix::WebSocketMessageType::Close)
|
||||||
{
|
{
|
||||||
Logger() << "Closed connection";
|
Logger() << "Closed connection";
|
||||||
}
|
}
|
||||||
else if (messageType == ix::WebSocket_MessageType_Message)
|
else if (messageType == ix::WebSocketMessageType::Message)
|
||||||
{
|
{
|
||||||
for (auto&& client : server.getClients())
|
for (auto&& client : server.getClients())
|
||||||
{
|
{
|
||||||
|
@ -60,30 +60,36 @@ namespace
|
|||||||
const ix::WebSocketCloseInfo& closeInfo)
|
const ix::WebSocketCloseInfo& closeInfo)
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
if (messageType == ix::WebSocket_MessageType_Open)
|
if (messageType == ix::WebSocketMessageType::Open)
|
||||||
{
|
{
|
||||||
log("cmd_websocket_satori_chat: connected !");
|
log("cmd_websocket_satori_chat: connected !");
|
||||||
}
|
}
|
||||||
else if (messageType == ix::WebSocket_MessageType_Close)
|
else if (messageType == ix::WebSocketMessageType::Close)
|
||||||
{
|
{
|
||||||
log("cmd_websocket_satori_chat: disconnected !");
|
log("cmd_websocket_satori_chat: disconnected !");
|
||||||
}
|
}
|
||||||
else if (messageType == ix::WebSocket_MessageType_Error)
|
else if (messageType == ix::WebSocketMessageType::Error)
|
||||||
{
|
{
|
||||||
log("cmd_websocket_satori_chat: Error!");
|
ss << "cmd_websocket_satori_chat: Error! ";
|
||||||
|
ss << error.reason;
|
||||||
|
log(ss.str());
|
||||||
}
|
}
|
||||||
else if (messageType == ix::WebSocket_MessageType_Message)
|
else if (messageType == ix::WebSocketMessageType::Message)
|
||||||
{
|
{
|
||||||
log("cmd_websocket_satori_chat: received message.!");
|
log("cmd_websocket_satori_chat: received message.!");
|
||||||
}
|
}
|
||||||
else if (messageType == ix::WebSocket_MessageType_Ping)
|
else if (messageType == ix::WebSocketMessageType::Ping)
|
||||||
{
|
{
|
||||||
log("cmd_websocket_satori_chat: received ping message.!");
|
log("cmd_websocket_satori_chat: received ping message.!");
|
||||||
}
|
}
|
||||||
else if (messageType == ix::WebSocket_MessageType_Pong)
|
else if (messageType == ix::WebSocketMessageType::Pong)
|
||||||
{
|
{
|
||||||
log("cmd_websocket_satori_chat: received pong message.!");
|
log("cmd_websocket_satori_chat: received pong message.!");
|
||||||
}
|
}
|
||||||
|
else if (messageType == ix::WebSocketMessageType::Fragment)
|
||||||
|
{
|
||||||
|
log("cmd_websocket_satori_chat: received fragment.!");
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
log("Invalid ix::WebSocketMessageType");
|
log("Invalid ix::WebSocketMessageType");
|
||||||
@ -103,26 +109,40 @@ TEST_CASE("websocket_connections", "[websocket]")
|
|||||||
{
|
{
|
||||||
SECTION("Try to connect to invalid servers.")
|
SECTION("Try to connect to invalid servers.")
|
||||||
{
|
{
|
||||||
IXWebSocketTestConnectionDisconnection chatA;
|
IXWebSocketTestConnectionDisconnection test;
|
||||||
|
|
||||||
chatA.start(GOOGLE_URL);
|
test.start(GOOGLE_URL);
|
||||||
ix::msleep(1000);
|
ix::msleep(1000);
|
||||||
chatA.stop();
|
test.stop();
|
||||||
|
|
||||||
chatA.start(UNKNOWN_URL);
|
test.start(UNKNOWN_URL);
|
||||||
ix::msleep(1000);
|
ix::msleep(1000);
|
||||||
chatA.stop();
|
test.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Try to connect and disconnect with different timing.")
|
SECTION("Try to connect and disconnect with different timing, not enough time to succesfully connect")
|
||||||
{
|
{
|
||||||
IXWebSocketTestConnectionDisconnection chatA;
|
IXWebSocketTestConnectionDisconnection test;
|
||||||
for (int i = 0; i < 50; ++i)
|
for (int i = 0; i < 50; ++i)
|
||||||
{
|
{
|
||||||
log(std::string("Run: ") + std::to_string(i));
|
log(std::string("Run: ") + std::to_string(i));
|
||||||
chatA.start(WEBSOCKET_DOT_ORG_URL);
|
test.start(WEBSOCKET_DOT_ORG_URL);
|
||||||
ix::msleep(i);
|
ix::msleep(i);
|
||||||
chatA.stop();
|
test.stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This test breaks on travis CI - Ubuntu Xenial + gcc + tsan
|
||||||
|
// We should fix this.
|
||||||
|
/*SECTION("Try to connect and disconnect with different timing, from not enough time to successfull connect")
|
||||||
|
{
|
||||||
|
IXWebSocketTestConnectionDisconnection test;
|
||||||
|
for (int i = 0; i < 20; ++i)
|
||||||
|
{
|
||||||
|
log(std::string("Run: ") + std::to_string(i));
|
||||||
|
test.start(WEBSOCKET_DOT_ORG_URL);
|
||||||
|
ix::msleep(i*50);
|
||||||
|
test.stop();
|
||||||
|
}
|
||||||
|
}*/
|
||||||
}
|
}
|
||||||
|
@ -87,7 +87,7 @@ namespace
|
|||||||
|
|
||||||
bool WebSocketChat::isReady() const
|
bool WebSocketChat::isReady() const
|
||||||
{
|
{
|
||||||
return _webSocket.getReadyState() == ix::WebSocket_ReadyState_Open;
|
return _webSocket.getReadyState() == ix::ReadyState::Open;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebSocketChat::stop()
|
void WebSocketChat::stop()
|
||||||
@ -122,21 +122,21 @@ namespace
|
|||||||
const ix::WebSocketCloseInfo& closeInfo)
|
const ix::WebSocketCloseInfo& closeInfo)
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
if (messageType == ix::WebSocket_MessageType_Open)
|
if (messageType == ix::WebSocketMessageType::Open)
|
||||||
{
|
{
|
||||||
ss << "cmd_websocket_chat: user "
|
ss << "cmd_websocket_chat: user "
|
||||||
<< _user
|
<< _user
|
||||||
<< " Connected !";
|
<< " Connected !";
|
||||||
log(ss.str());
|
log(ss.str());
|
||||||
}
|
}
|
||||||
else if (messageType == ix::WebSocket_MessageType_Close)
|
else if (messageType == ix::WebSocketMessageType::Close)
|
||||||
{
|
{
|
||||||
ss << "cmd_websocket_chat: user "
|
ss << "cmd_websocket_chat: user "
|
||||||
<< _user
|
<< _user
|
||||||
<< " disconnected !";
|
<< " disconnected !";
|
||||||
log(ss.str());
|
log(ss.str());
|
||||||
}
|
}
|
||||||
else if (messageType == ix::WebSocket_MessageType_Message)
|
else if (messageType == ix::WebSocketMessageType::Message)
|
||||||
{
|
{
|
||||||
auto result = decodeMessage(str);
|
auto result = decodeMessage(str);
|
||||||
|
|
||||||
@ -159,20 +159,20 @@ namespace
|
|||||||
<< _user << " > ";
|
<< _user << " > ";
|
||||||
log(ss.str());
|
log(ss.str());
|
||||||
}
|
}
|
||||||
else if (messageType == ix::WebSocket_MessageType_Error)
|
else if (messageType == ix::WebSocketMessageType::Error)
|
||||||
{
|
{
|
||||||
ss << "cmd_websocket_chat: Error ! " << error.reason;
|
ss << "cmd_websocket_chat: Error ! " << error.reason;
|
||||||
log(ss.str());
|
log(ss.str());
|
||||||
}
|
}
|
||||||
else if (messageType == ix::WebSocket_MessageType_Ping)
|
else if (messageType == ix::WebSocketMessageType::Ping)
|
||||||
{
|
{
|
||||||
log("cmd_websocket_chat: received ping message");
|
log("cmd_websocket_chat: received ping message");
|
||||||
}
|
}
|
||||||
else if (messageType == ix::WebSocket_MessageType_Pong)
|
else if (messageType == ix::WebSocketMessageType::Pong)
|
||||||
{
|
{
|
||||||
log("cmd_websocket_chat: received pong message");
|
log("cmd_websocket_chat: received pong message");
|
||||||
}
|
}
|
||||||
else if (messageType == ix::WebSocket_MessageType_Fragment)
|
else if (messageType == ix::WebSocketMessageType::Fragment)
|
||||||
{
|
{
|
||||||
log("cmd_websocket_chat: received message fragment");
|
log("cmd_websocket_chat: received message fragment");
|
||||||
}
|
}
|
||||||
@ -228,7 +228,7 @@ namespace
|
|||||||
const ix::WebSocketOpenInfo& openInfo,
|
const ix::WebSocketOpenInfo& openInfo,
|
||||||
const ix::WebSocketCloseInfo& closeInfo)
|
const ix::WebSocketCloseInfo& closeInfo)
|
||||||
{
|
{
|
||||||
if (messageType == ix::WebSocket_MessageType_Open)
|
if (messageType == ix::WebSocketMessageType::Open)
|
||||||
{
|
{
|
||||||
Logger() << "New connection";
|
Logger() << "New connection";
|
||||||
Logger() << "id: " << connectionState->getId();
|
Logger() << "id: " << connectionState->getId();
|
||||||
@ -239,11 +239,11 @@ namespace
|
|||||||
Logger() << it.first << ": " << it.second;
|
Logger() << it.first << ": " << it.second;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (messageType == ix::WebSocket_MessageType_Close)
|
else if (messageType == ix::WebSocketMessageType::Close)
|
||||||
{
|
{
|
||||||
log("Closed connection");
|
log("Closed connection");
|
||||||
}
|
}
|
||||||
else if (messageType == ix::WebSocket_MessageType_Message)
|
else if (messageType == ix::WebSocketMessageType::Message)
|
||||||
{
|
{
|
||||||
for (auto&& client : server.getClients())
|
for (auto&& client : server.getClients())
|
||||||
{
|
{
|
||||||
|
@ -7,12 +7,21 @@
|
|||||||
#define CATCH_CONFIG_RUNNER
|
#define CATCH_CONFIG_RUNNER
|
||||||
#include "catch.hpp"
|
#include "catch.hpp"
|
||||||
|
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
#include <ixwebsocket/IXNetSystem.h>
|
#include <ixwebsocket/IXNetSystem.h>
|
||||||
|
#include <ixcore/utils/IXCoreLogger.h>
|
||||||
|
|
||||||
int main(int argc, char* argv[])
|
int main(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
ix::initNetSystem();
|
ix::initNetSystem();
|
||||||
|
|
||||||
|
ix::IXCoreLogger::LogFunc logFunc = [](const char* msg)
|
||||||
|
{
|
||||||
|
spdlog::info(msg);
|
||||||
|
};
|
||||||
|
ix::IXCoreLogger::setLogFunction(logFunc);
|
||||||
|
|
||||||
int result = Catch::Session().run(argc, argv);
|
int result = Catch::Session().run(argc, argv);
|
||||||
|
|
||||||
ix::uninitNetSystem();
|
ix::uninitNetSystem();
|
||||||
|
4
third_party/README.md
vendored
4
third_party/README.md
vendored
@ -1 +1,3 @@
|
|||||||
Except ZLIB on Windows (whose port is currently broken...) all dependencies here are for the ws command line tools, not for the IXWebSockets library which is standalone.
|
# Note
|
||||||
|
|
||||||
|
Except *zlib* on Windows, all dependencies here are for the ws command line tools, not for the IXWebSockets library which is standalone.
|
||||||
|
58
third_party/msgpack11/msgpack11.cpp
vendored
58
third_party/msgpack11/msgpack11.cpp
vendored
@ -119,62 +119,62 @@ static void verify_length(size_t len)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void dump(NullStruct, std::string &out) {
|
static void dump(NullStruct, std::string &out) {
|
||||||
out.push_back(0xc0);
|
out.push_back((char) 0xc0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dump(float value, std::string &out) {
|
static void dump(float value, std::string &out) {
|
||||||
out.push_back(0xca);
|
out.push_back((char) 0xca);
|
||||||
dump_data(value, out);
|
dump_data(value, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dump(double value, std::string &out) {
|
static void dump(double value, std::string &out) {
|
||||||
out.push_back(0xcb);
|
out.push_back((char) 0xcb);
|
||||||
dump_data(value, out);
|
dump_data(value, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dump(int8_t value, std::string &out) {
|
static void dump(int8_t value, std::string &out) {
|
||||||
if( value < -32 )
|
if( value < -32 )
|
||||||
{
|
{
|
||||||
out.push_back(0xd0);
|
out.push_back((char) 0xd0);
|
||||||
}
|
}
|
||||||
out.push_back(value);
|
out.push_back(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dump(int16_t value, std::string &out) {
|
static void dump(int16_t value, std::string &out) {
|
||||||
out.push_back(0xd1);
|
out.push_back((char) 0xd1);
|
||||||
dump_data(value, out);
|
dump_data(value, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dump(int32_t value, std::string &out) {
|
static void dump(int32_t value, std::string &out) {
|
||||||
out.push_back(0xd2);
|
out.push_back((char) 0xd2);
|
||||||
dump_data(value, out);
|
dump_data(value, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dump(int64_t value, std::string &out) {
|
static void dump(int64_t value, std::string &out) {
|
||||||
out.push_back(0xd3);
|
out.push_back((char) 0xd3);
|
||||||
dump_data(value, out);
|
dump_data(value, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dump(uint8_t value, std::string &out) {
|
static void dump(uint8_t value, std::string &out) {
|
||||||
if(128 <= value)
|
if(128 <= value)
|
||||||
{
|
{
|
||||||
out.push_back(0xcc);
|
out.push_back((char) 0xcc);
|
||||||
}
|
}
|
||||||
out.push_back(value);
|
out.push_back(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dump(uint16_t value, std::string &out) {
|
static void dump(uint16_t value, std::string &out) {
|
||||||
out.push_back(0xcd);
|
out.push_back((char) 0xcd);
|
||||||
dump_data(value, out);
|
dump_data(value, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dump(uint32_t value, std::string &out) {
|
static void dump(uint32_t value, std::string &out) {
|
||||||
out.push_back(0xce);
|
out.push_back((char) 0xce);
|
||||||
dump_data(value, out);
|
dump_data(value, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dump(uint64_t value, std::string &out) {
|
static void dump(uint64_t value, std::string &out) {
|
||||||
out.push_back(0xcf);
|
out.push_back((char) 0xcf);
|
||||||
dump_data(value, out);
|
dump_data(value, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -194,19 +194,19 @@ static void dump(const std::string& value, std::string &out) {
|
|||||||
else if(len <= 0xff)
|
else if(len <= 0xff)
|
||||||
{
|
{
|
||||||
uint8_t const length = static_cast<uint8_t>(len);
|
uint8_t const length = static_cast<uint8_t>(len);
|
||||||
out.push_back(0xd9);
|
out.push_back((char) 0xd9);
|
||||||
out.push_back(length);
|
out.push_back(length);
|
||||||
}
|
}
|
||||||
else if(len <= 0xffff)
|
else if(len <= 0xffff)
|
||||||
{
|
{
|
||||||
uint16_t const length = static_cast<uint16_t>(len);
|
uint16_t const length = static_cast<uint16_t>(len);
|
||||||
out.push_back(0xda);
|
out.push_back((char) 0xda);
|
||||||
dump_data(length, out);
|
dump_data(length, out);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
uint32_t const length = static_cast<uint32_t>(len);
|
uint32_t const length = static_cast<uint32_t>(len);
|
||||||
out.push_back(0xdb);
|
out.push_back((char) 0xdb);
|
||||||
dump_data(length, out);
|
dump_data(length, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -226,13 +226,13 @@ static void dump(const MsgPack::array& value, std::string &out) {
|
|||||||
else if(len <= 0xffff)
|
else if(len <= 0xffff)
|
||||||
{
|
{
|
||||||
uint16_t const length = static_cast<uint16_t>(len);
|
uint16_t const length = static_cast<uint16_t>(len);
|
||||||
out.push_back(0xdc);
|
out.push_back((char) 0xdc);
|
||||||
dump_data(length, out);
|
dump_data(length, out);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
uint32_t const length = static_cast<uint32_t>(len);
|
uint32_t const length = static_cast<uint32_t>(len);
|
||||||
out.push_back(0xdd);
|
out.push_back((char) 0xdd);
|
||||||
dump_data(length, out);
|
dump_data(length, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -252,13 +252,13 @@ static void dump(const MsgPack::object& value, std::string &out) {
|
|||||||
else if(len <= 0xffff)
|
else if(len <= 0xffff)
|
||||||
{
|
{
|
||||||
uint16_t const length = static_cast<uint16_t>(len);
|
uint16_t const length = static_cast<uint16_t>(len);
|
||||||
out.push_back(0xde);
|
out.push_back((char) 0xde);
|
||||||
dump_data(length, out);
|
dump_data(length, out);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
uint32_t const length = static_cast<uint32_t>(len);
|
uint32_t const length = static_cast<uint32_t>(len);
|
||||||
out.push_back(0xdf);
|
out.push_back((char) 0xdf);
|
||||||
dump_data(length, out);
|
dump_data(length, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -274,19 +274,19 @@ static void dump(const MsgPack::binary& value, std::string &out) {
|
|||||||
if(len <= 0xff)
|
if(len <= 0xff)
|
||||||
{
|
{
|
||||||
uint8_t const length = static_cast<uint8_t>(len);
|
uint8_t const length = static_cast<uint8_t>(len);
|
||||||
out.push_back(0xc4);
|
out.push_back((char) 0xc4);
|
||||||
dump_data(length, out);
|
dump_data(length, out);
|
||||||
}
|
}
|
||||||
else if(len <= 0xffff)
|
else if(len <= 0xffff)
|
||||||
{
|
{
|
||||||
uint16_t const length = static_cast<uint16_t>(len);
|
uint16_t const length = static_cast<uint16_t>(len);
|
||||||
out.push_back(0xc5);
|
out.push_back((char) 0xc5);
|
||||||
dump_data(length, out);
|
dump_data(length, out);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
uint32_t const length = static_cast<uint32_t>(len);
|
uint32_t const length = static_cast<uint32_t>(len);
|
||||||
out.push_back(0xc6);
|
out.push_back((char) 0xc6);
|
||||||
dump_data(length, out);
|
dump_data(length, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -302,33 +302,33 @@ static void dump(const MsgPack::extension& value, std::string &out) {
|
|||||||
verify_length(len);
|
verify_length(len);
|
||||||
|
|
||||||
if(len == 0x01) {
|
if(len == 0x01) {
|
||||||
out.push_back(0xd4);
|
out.push_back((char) 0xd4);
|
||||||
}
|
}
|
||||||
else if(len == 0x02) {
|
else if(len == 0x02) {
|
||||||
out.push_back(0xd5);
|
out.push_back((char) 0xd5);
|
||||||
}
|
}
|
||||||
else if(len == 0x04) {
|
else if(len == 0x04) {
|
||||||
out.push_back(0xd6);
|
out.push_back((char) 0xd6);
|
||||||
}
|
}
|
||||||
else if(len == 0x08) {
|
else if(len == 0x08) {
|
||||||
out.push_back(0xd7);
|
out.push_back((char) 0xd7);
|
||||||
}
|
}
|
||||||
else if(len == 0x10) {
|
else if(len == 0x10) {
|
||||||
out.push_back(0xd8);
|
out.push_back((char) 0xd8);
|
||||||
}
|
}
|
||||||
else if(len <= 0xff) {
|
else if(len <= 0xff) {
|
||||||
uint8_t const length = static_cast<uint8_t>(len);
|
uint8_t const length = static_cast<uint8_t>(len);
|
||||||
out.push_back(0xc7);
|
out.push_back((char) 0xc7);
|
||||||
out.push_back(length);
|
out.push_back(length);
|
||||||
}
|
}
|
||||||
else if(len <= 0xffff) {
|
else if(len <= 0xffff) {
|
||||||
uint16_t const length = static_cast<uint16_t>(len);
|
uint16_t const length = static_cast<uint16_t>(len);
|
||||||
out.push_back(0xc8);
|
out.push_back((char) 0xc8);
|
||||||
dump_data(length, out);
|
dump_data(length, out);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
uint32_t const length = static_cast<uint32_t>(len);
|
uint32_t const length = static_cast<uint32_t>(len);
|
||||||
out.push_back(0xc9);
|
out.push_back((char) 0xc9);
|
||||||
dump_data(length, out);
|
dump_data(length, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
108
third_party/spdlog/.clang-format
vendored
Normal file
108
third_party/spdlog/.clang-format
vendored
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
---
|
||||||
|
Language: Cpp
|
||||||
|
# BasedOnStyle: LLVM
|
||||||
|
AccessModifierOffset: -4
|
||||||
|
AlignAfterOpenBracket: DontAlign
|
||||||
|
AlignConsecutiveAssignments: false
|
||||||
|
AlignConsecutiveDeclarations: false
|
||||||
|
AlignEscapedNewlines: Right
|
||||||
|
AlignOperands: true
|
||||||
|
AlignTrailingComments: true
|
||||||
|
AllowAllParametersOfDeclarationOnNextLine: true
|
||||||
|
AllowShortBlocksOnASingleLine: true
|
||||||
|
AllowShortCaseLabelsOnASingleLine: false
|
||||||
|
AllowShortFunctionsOnASingleLine: Empty
|
||||||
|
AllowShortIfStatementsOnASingleLine: false
|
||||||
|
AllowShortLoopsOnASingleLine: false
|
||||||
|
AlwaysBreakAfterDefinitionReturnType: None
|
||||||
|
AlwaysBreakAfterReturnType: None
|
||||||
|
AlwaysBreakBeforeMultilineStrings: false
|
||||||
|
AlwaysBreakTemplateDeclarations: true
|
||||||
|
BinPackArguments: true
|
||||||
|
BinPackParameters: true
|
||||||
|
BraceWrapping:
|
||||||
|
AfterClass: true
|
||||||
|
AfterControlStatement: true
|
||||||
|
AfterEnum: true
|
||||||
|
AfterFunction: true
|
||||||
|
AfterNamespace: false
|
||||||
|
AfterObjCDeclaration: true
|
||||||
|
AfterStruct: true
|
||||||
|
AfterUnion: true
|
||||||
|
BeforeCatch: true
|
||||||
|
BeforeElse: true
|
||||||
|
IndentBraces: false
|
||||||
|
SplitEmptyFunction: true
|
||||||
|
SplitEmptyRecord: true
|
||||||
|
SplitEmptyNamespace: true
|
||||||
|
BreakBeforeBinaryOperators: None
|
||||||
|
BreakBeforeBraces: Custom
|
||||||
|
BreakBeforeInheritanceComma: false
|
||||||
|
BreakBeforeTernaryOperators: true
|
||||||
|
BreakConstructorInitializersBeforeComma: true
|
||||||
|
BreakConstructorInitializers: BeforeColon
|
||||||
|
BreakAfterJavaFieldAnnotations: false
|
||||||
|
BreakStringLiterals: true
|
||||||
|
ColumnLimit: 140
|
||||||
|
CommentPragmas: '^ IWYU pragma:'
|
||||||
|
CompactNamespaces: false
|
||||||
|
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||||
|
ConstructorInitializerIndentWidth: 4
|
||||||
|
ContinuationIndentWidth: 4
|
||||||
|
Cpp11BracedListStyle: true
|
||||||
|
DerivePointerAlignment: false
|
||||||
|
DisableFormat: false
|
||||||
|
ExperimentalAutoDetectBinPacking: false
|
||||||
|
FixNamespaceComments: true
|
||||||
|
ForEachMacros:
|
||||||
|
- foreach
|
||||||
|
- Q_FOREACH
|
||||||
|
- BOOST_FOREACH
|
||||||
|
IncludeCategories:
|
||||||
|
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
|
||||||
|
Priority: 2
|
||||||
|
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
|
||||||
|
Priority: 3
|
||||||
|
- Regex: '.*'
|
||||||
|
Priority: 1
|
||||||
|
IncludeIsMainRegex: '(Test)?$'
|
||||||
|
IndentCaseLabels: false
|
||||||
|
IndentWidth: 4
|
||||||
|
IndentWrappedFunctionNames: false
|
||||||
|
JavaScriptQuotes: Leave
|
||||||
|
JavaScriptWrapImports: true
|
||||||
|
KeepEmptyLinesAtTheStartOfBlocks: true
|
||||||
|
MacroBlockBegin: ''
|
||||||
|
MacroBlockEnd: ''
|
||||||
|
MaxEmptyLinesToKeep: 1
|
||||||
|
NamespaceIndentation: None
|
||||||
|
ObjCBlockIndentWidth: 2
|
||||||
|
ObjCSpaceAfterProperty: false
|
||||||
|
ObjCSpaceBeforeProtocolList: true
|
||||||
|
PenaltyBreakAssignment: 2
|
||||||
|
PenaltyBreakBeforeFirstCallParameter: 19
|
||||||
|
PenaltyBreakComment: 300
|
||||||
|
PenaltyBreakFirstLessLess: 120
|
||||||
|
PenaltyBreakString: 1000
|
||||||
|
PenaltyExcessCharacter: 1000000
|
||||||
|
PenaltyReturnTypeOnItsOwnLine: 60
|
||||||
|
PointerAlignment: Right
|
||||||
|
ReflowComments: true
|
||||||
|
SortIncludes: false
|
||||||
|
SortUsingDeclarations: true
|
||||||
|
SpaceAfterCStyleCast: false
|
||||||
|
SpaceAfterTemplateKeyword: false
|
||||||
|
SpaceBeforeAssignmentOperators: true
|
||||||
|
SpaceBeforeParens: ControlStatements
|
||||||
|
SpaceInEmptyParentheses: false
|
||||||
|
SpacesBeforeTrailingComments: 1
|
||||||
|
SpacesInAngles: false
|
||||||
|
SpacesInContainerLiterals: true
|
||||||
|
SpacesInCStyleCastParentheses: false
|
||||||
|
SpacesInParentheses: false
|
||||||
|
SpacesInSquareBrackets: false
|
||||||
|
Standard: Cpp11
|
||||||
|
TabWidth: 8
|
||||||
|
UseTab: Never
|
||||||
|
...
|
||||||
|
|
28
third_party/spdlog/.clang-tidy
vendored
Normal file
28
third_party/spdlog/.clang-tidy
vendored
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
Checks: 'modernize-*,modernize-use-override,google-*,-google-runtime-references,misc-*,clang-analyzer-*'
|
||||||
|
WarningsAsErrors: ''
|
||||||
|
HeaderFilterRegex: 'async.h|async_logger.h|common.h|details|formatter.h|logger.h|sinks|spdlog.h|tweakme.h|version.h'
|
||||||
|
AnalyzeTemporaryDtors: false
|
||||||
|
FormatStyle: none
|
||||||
|
|
||||||
|
CheckOptions:
|
||||||
|
- key: google-readability-braces-around-statements.ShortStatementLines
|
||||||
|
value: '1'
|
||||||
|
- key: google-readability-function-size.StatementThreshold
|
||||||
|
value: '800'
|
||||||
|
- key: google-readability-namespace-comments.ShortNamespaceLines
|
||||||
|
value: '10'
|
||||||
|
- key: google-readability-namespace-comments.SpacesBeforeComments
|
||||||
|
value: '2'
|
||||||
|
- key: modernize-loop-convert.MaxCopySize
|
||||||
|
value: '16'
|
||||||
|
- key: modernize-loop-convert.MinConfidence
|
||||||
|
value: reasonable
|
||||||
|
- key: modernize-loop-convert.NamingStyle
|
||||||
|
value: CamelCase
|
||||||
|
- key: modernize-pass-by-value.IncludeStyle
|
||||||
|
value: llvm
|
||||||
|
- key: modernize-replace-auto-ptr.IncludeStyle
|
||||||
|
value: llvm
|
||||||
|
- key: modernize-use-nullptr.NullMacros
|
||||||
|
value: 'NULL'
|
||||||
|
|
68
third_party/spdlog/.gitignore
vendored
Normal file
68
third_party/spdlog/.gitignore
vendored
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
# Auto generated files
|
||||||
|
build/*
|
||||||
|
*.slo
|
||||||
|
*.lo
|
||||||
|
*.o
|
||||||
|
*.obj
|
||||||
|
*.suo
|
||||||
|
*.tlog
|
||||||
|
*.ilk
|
||||||
|
*.log
|
||||||
|
*.pdb
|
||||||
|
*.idb
|
||||||
|
*.iobj
|
||||||
|
*.ipdb
|
||||||
|
*.opensdf
|
||||||
|
*.sdf
|
||||||
|
|
||||||
|
# Compiled Dynamic libraries
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
*.dll
|
||||||
|
|
||||||
|
# Compiled Static libraries
|
||||||
|
*.lai
|
||||||
|
*.la
|
||||||
|
*.a
|
||||||
|
*.lib
|
||||||
|
|
||||||
|
# Executables
|
||||||
|
*.exe
|
||||||
|
*.out
|
||||||
|
*.app
|
||||||
|
|
||||||
|
# Codelite
|
||||||
|
.codelite
|
||||||
|
|
||||||
|
# .orig files
|
||||||
|
*.orig
|
||||||
|
|
||||||
|
# example files
|
||||||
|
example/*
|
||||||
|
!example/example.cpp
|
||||||
|
!example/bench.cpp
|
||||||
|
!example/utils.h
|
||||||
|
!example/Makefile*
|
||||||
|
!example/example.sln
|
||||||
|
!example/example.vcxproj
|
||||||
|
!example/CMakeLists.txt
|
||||||
|
!example/multisink.cpp
|
||||||
|
!example/jni
|
||||||
|
|
||||||
|
# generated files
|
||||||
|
generated
|
||||||
|
|
||||||
|
# Cmake
|
||||||
|
CMakeCache.txt
|
||||||
|
CMakeFiles
|
||||||
|
CMakeScripts
|
||||||
|
Makefile
|
||||||
|
cmake_install.cmake
|
||||||
|
install_manifest.txt
|
||||||
|
/tests/tests.VC.VC.opendb
|
||||||
|
/tests/tests.VC.db
|
||||||
|
/tests/tests
|
||||||
|
/tests/logs/*
|
||||||
|
|
||||||
|
# idea
|
||||||
|
.idea/
|
116
third_party/spdlog/.travis.yml
vendored
Normal file
116
third_party/spdlog/.travis.yml
vendored
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
# Adapted from various sources, including:
|
||||||
|
# - Louis Dionne's Hana: https://github.com/ldionne/hana
|
||||||
|
# - Paul Fultz II's FIT: https://github.com/pfultz2/Fit
|
||||||
|
# - Eric Niebler's range-v3: https://github.com/ericniebler/range-v3
|
||||||
|
sudo: required
|
||||||
|
language: cpp
|
||||||
|
|
||||||
|
addons: &gcc48
|
||||||
|
apt:
|
||||||
|
packages:
|
||||||
|
- g++-4.8
|
||||||
|
sources:
|
||||||
|
- ubuntu-toolchain-r-test
|
||||||
|
|
||||||
|
addons: &gcc7
|
||||||
|
apt:
|
||||||
|
packages:
|
||||||
|
- g++-7
|
||||||
|
sources:
|
||||||
|
- ubuntu-toolchain-r-test
|
||||||
|
|
||||||
|
addons: &clang35
|
||||||
|
apt:
|
||||||
|
packages:
|
||||||
|
- clang-3.5
|
||||||
|
sources:
|
||||||
|
- ubuntu-toolchain-r-test
|
||||||
|
- llvm-toolchain-precise-3.5
|
||||||
|
|
||||||
|
addons: &clang6
|
||||||
|
apt:
|
||||||
|
packages:
|
||||||
|
- clang-6.0
|
||||||
|
sources:
|
||||||
|
- ubuntu-toolchain-r-test
|
||||||
|
- llvm-toolchain-trusty-6.0
|
||||||
|
|
||||||
|
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
# Test gcc-4.8: C++11, Build=Debug/Release
|
||||||
|
- env: GCC_VERSION=4.8 BUILD_TYPE=Debug CPP=11
|
||||||
|
os: linux
|
||||||
|
addons: *gcc48
|
||||||
|
|
||||||
|
- env: GCC_VERSION=4.8 BUILD_TYPE=Release CPP=11
|
||||||
|
os: linux
|
||||||
|
addons: *gcc48
|
||||||
|
|
||||||
|
- env: GCC_VERSION=7 BUILD_TYPE=Release CPP=11
|
||||||
|
os: linux
|
||||||
|
addons: *gcc7
|
||||||
|
|
||||||
|
# Test clang-3.5: C++11, Build=Debug/Release
|
||||||
|
- env: CLANG_VERSION=3.5 BUILD_TYPE=Debug CPP=11
|
||||||
|
os: linux
|
||||||
|
addons: *clang35
|
||||||
|
|
||||||
|
- env: CLANG_VERSION=3.5 BUILD_TYPE=Release CPP=11
|
||||||
|
os: linux
|
||||||
|
addons: *clang35
|
||||||
|
|
||||||
|
# Test clang-6.0: C++11, Build=Debug, ASAN=On
|
||||||
|
- env: CLANG_VERSION=6.0 BUILD_TYPE=Debug CPP=11 ASAN=On TSAN=Off
|
||||||
|
os: linux
|
||||||
|
addons: *clang6
|
||||||
|
|
||||||
|
- env: CLANG_VERSION=6.0 BUILD_TYPE=Release CPP=11 ASAN=On TSAN=Off
|
||||||
|
os: linux
|
||||||
|
addons: *clang6
|
||||||
|
|
||||||
|
# Test clang-6.0: C++11, Build=Debug, TSAN=On
|
||||||
|
- env: CLANG_VERSION=6.0 BUILD_TYPE=Debug CPP=11 ASAN=Off TSAN=On
|
||||||
|
os: linux
|
||||||
|
addons: *clang6
|
||||||
|
|
||||||
|
- env: CLANG_VERSION=6.0 BUILD_TYPE=Release CPP=11 ASAN=Off TSAN=On
|
||||||
|
os: linux
|
||||||
|
addons: *clang6
|
||||||
|
|
||||||
|
# osx
|
||||||
|
- env: BUILD_TYPE=Release CPP=11 ASAN=Off TSAN=Off
|
||||||
|
os: osx
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
before_script:
|
||||||
|
- if [ -n "$GCC_VERSION" ]; then export CXX="g++-${GCC_VERSION}" CC="gcc-${GCC_VERSION}"; fi
|
||||||
|
- if [ -n "$CLANG_VERSION" ]; then export CXX="clang++-${CLANG_VERSION}" CC="clang-${CLANG_VERSION}"; fi
|
||||||
|
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then export CXX="clang++" CC="clang"; fi
|
||||||
|
- which $CXX
|
||||||
|
- which $CC
|
||||||
|
- $CXX --version
|
||||||
|
- cmake --version
|
||||||
|
|
||||||
|
script:
|
||||||
|
- cd ${TRAVIS_BUILD_DIR}
|
||||||
|
- mkdir -p build && cd build
|
||||||
|
- |
|
||||||
|
cmake .. \
|
||||||
|
--warn-uninitialized \
|
||||||
|
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \
|
||||||
|
-DCMAKE_CXX_STANDARD=$CPP \
|
||||||
|
-DSPDLOG_BUILD_EXAMPLES=ON \
|
||||||
|
-DSPDLOG_BUILD_BENCH=OFF \
|
||||||
|
-DSPDLOG_BUILD_TESTS=ON \
|
||||||
|
-DSPDLOG_SANITIZE_ADDRESS=$ASAN \
|
||||||
|
-DSPDLOG_SANITIZE_THREAD=$TSAN
|
||||||
|
- make VERBOSE=1 -j2
|
||||||
|
- ctest -j2 --output-on-failure
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
notifications:
|
||||||
|
email: false
|
157
third_party/spdlog/CMakeLists.txt
vendored
Normal file
157
third_party/spdlog/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
#
|
||||||
|
# Copyright(c) 2015 Ruslan Baratov.
|
||||||
|
# Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
#
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 3.1)
|
||||||
|
project(spdlog VERSION 1.3.1 LANGUAGES CXX)
|
||||||
|
include(CMakeDependentOption)
|
||||||
|
include(GNUInstallDirs)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------------
|
||||||
|
# set default build to release
|
||||||
|
#---------------------------------------------------------------------------------------
|
||||||
|
if(NOT CMAKE_BUILD_TYPE)
|
||||||
|
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose Release or Debug" FORCE)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
message(STATUS "Build type: " ${CMAKE_BUILD_TYPE})
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------------
|
||||||
|
# compiler config
|
||||||
|
#---------------------------------------------------------------------------------------
|
||||||
|
set(CMAKE_CXX_STANDARD 11)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||||
|
|
||||||
|
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
|
||||||
|
add_compile_options("-Wall")
|
||||||
|
add_compile_options("-Wextra")
|
||||||
|
add_compile_options("-Wconversion")
|
||||||
|
add_compile_options("-pedantic")
|
||||||
|
add_compile_options("-Wfatal-errors")
|
||||||
|
|
||||||
|
endif()
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------------
|
||||||
|
# address sanitizers check
|
||||||
|
#---------------------------------------------------------------------------------------
|
||||||
|
include(cmake/sanitizers.cmake)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------------
|
||||||
|
# spdlog target
|
||||||
|
#---------------------------------------------------------------------------------------
|
||||||
|
add_library(spdlog INTERFACE)
|
||||||
|
add_library(spdlog::spdlog ALIAS spdlog)
|
||||||
|
|
||||||
|
# Check if spdlog is being used directly or via add_subdirectory
|
||||||
|
set(SPDLOG_MASTER_PROJECT OFF)
|
||||||
|
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
|
||||||
|
set(SPDLOG_MASTER_PROJECT ON)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
option(SPDLOG_BUILD_EXAMPLES "Build examples" ${SPDLOG_MASTER_PROJECT})
|
||||||
|
option(SPDLOG_BUILD_BENCH "Build benchmarks" ${SPDLOG_MASTER_PROJECT})
|
||||||
|
option(SPDLOG_BUILD_TESTS "Build tests" ${SPDLOG_MASTER_PROJECT})
|
||||||
|
option(SPDLOG_FMT_EXTERNAL "Use external fmt library instead of bundled" OFF)
|
||||||
|
|
||||||
|
if(SPDLOG_FMT_EXTERNAL)
|
||||||
|
find_package(fmt REQUIRED CONFIG)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
target_include_directories(
|
||||||
|
spdlog
|
||||||
|
INTERFACE
|
||||||
|
"$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>"
|
||||||
|
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
|
||||||
|
)
|
||||||
|
|
||||||
|
if(SPDLOG_FMT_EXTERNAL)
|
||||||
|
target_compile_definitions(spdlog INTERFACE SPDLOG_FMT_EXTERNAL)
|
||||||
|
target_link_libraries(spdlog INTERFACE fmt::fmt)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(HEADER_BASE "${CMAKE_CURRENT_SOURCE_DIR}/include")
|
||||||
|
|
||||||
|
if(SPDLOG_BUILD_EXAMPLES)
|
||||||
|
add_subdirectory(example)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(SPDLOG_BUILD_TESTS)
|
||||||
|
include(CTest)
|
||||||
|
add_subdirectory(tests)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(SPDLOG_BUILD_BENCH)
|
||||||
|
add_subdirectory(bench)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------------
|
||||||
|
# Install/export targets and files
|
||||||
|
#---------------------------------------------------------------------------------------
|
||||||
|
# set files and directories
|
||||||
|
set(config_install_dir "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
|
||||||
|
set(include_install_dir "${CMAKE_INSTALL_INCLUDEDIR}")
|
||||||
|
set(pkgconfig_install_dir "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
|
||||||
|
set(version_config "${CMAKE_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake")
|
||||||
|
set(project_config "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake")
|
||||||
|
set(targets_config "${PROJECT_NAME}Targets.cmake")
|
||||||
|
set(pkg_config "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.pc")
|
||||||
|
set(targets_export_name "${PROJECT_NAME}Targets")
|
||||||
|
set(namespace "${PROJECT_NAME}::")
|
||||||
|
|
||||||
|
# generate package version file
|
||||||
|
include(CMakePackageConfigHelpers)
|
||||||
|
write_basic_package_version_file(
|
||||||
|
"${version_config}" COMPATIBILITY SameMajorVersion
|
||||||
|
)
|
||||||
|
|
||||||
|
# configure pkg config file
|
||||||
|
configure_file("cmake/spdlog.pc.in" "${pkg_config}" @ONLY)
|
||||||
|
# configure spdlogConfig.cmake file
|
||||||
|
configure_file("cmake/Config.cmake.in" "${project_config}" @ONLY)
|
||||||
|
|
||||||
|
# install targets
|
||||||
|
install(
|
||||||
|
TARGETS spdlog
|
||||||
|
EXPORT "${targets_export_name}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# install headers
|
||||||
|
install(
|
||||||
|
DIRECTORY "${HEADER_BASE}/${PROJECT_NAME}"
|
||||||
|
DESTINATION "${include_install_dir}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# install project config and version file
|
||||||
|
install(
|
||||||
|
FILES "${project_config}" "${version_config}"
|
||||||
|
DESTINATION "${config_install_dir}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# install pkg config file
|
||||||
|
install(
|
||||||
|
FILES "${pkg_config}"
|
||||||
|
DESTINATION "${pkgconfig_install_dir}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# install targets config file
|
||||||
|
install(
|
||||||
|
EXPORT "${targets_export_name}"
|
||||||
|
NAMESPACE "${namespace}"
|
||||||
|
DESTINATION "${config_install_dir}"
|
||||||
|
FILE ${targets_config}
|
||||||
|
)
|
||||||
|
|
||||||
|
# export build directory targets file
|
||||||
|
export(
|
||||||
|
EXPORT ${targets_export_name}
|
||||||
|
NAMESPACE "${namespace}"
|
||||||
|
FILE ${targets_config}
|
||||||
|
)
|
||||||
|
|
||||||
|
# register project in CMake user registry
|
||||||
|
export(PACKAGE ${PROJECT_NAME})
|
||||||
|
|
||||||
|
file(GLOB_RECURSE spdlog_include_SRCS "${HEADER_BASE}/*.h")
|
||||||
|
add_custom_target(spdlog_headers_for_ide SOURCES ${spdlog_include_SRCS})
|
13
third_party/spdlog/INSTALL
vendored
Normal file
13
third_party/spdlog/INSTALL
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
spdlog is header only library.
|
||||||
|
Just copy the files to your build tree and use a C++11 compiler
|
||||||
|
|
||||||
|
Tested on:
|
||||||
|
gcc 4.8.1 and above
|
||||||
|
clang 3.5
|
||||||
|
Visual Studio 2013
|
||||||
|
|
||||||
|
gcc 4.8 flags: --std==c++11 -pthread -O3 -flto -Wl,--no-as-needed
|
||||||
|
gcc 4.9 flags: --std=c++11 -pthread -O3 -flto
|
||||||
|
|
||||||
|
|
||||||
|
see the makefile in the example folder
|
22
third_party/spdlog/LICENSE
vendored
Normal file
22
third_party/spdlog/LICENSE
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2016 Gabi Melman.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
|
319
third_party/spdlog/README.md
vendored
Normal file
319
third_party/spdlog/README.md
vendored
Normal file
@ -0,0 +1,319 @@
|
|||||||
|
# spdlog
|
||||||
|
|
||||||
|
Very fast, header only, C++ logging library. [](https://travis-ci.org/gabime/spdlog) [](https://ci.appveyor.com/project/gabime/spdlog)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Install
|
||||||
|
#### Just copy the headers:
|
||||||
|
|
||||||
|
* Copy the source [folder](https://github.com/gabime/spdlog/tree/v1.x/include/spdlog) to your build tree and use a C++11 compiler.
|
||||||
|
|
||||||
|
#### Or use your favorite package manager:
|
||||||
|
|
||||||
|
* Ubuntu: `apt-get install libspdlog-dev`
|
||||||
|
* Homebrew: `brew install spdlog`
|
||||||
|
* FreeBSD: `cd /usr/ports/devel/spdlog/ && make install clean`
|
||||||
|
* Fedora: `yum install spdlog`
|
||||||
|
* Gentoo: `emerge dev-libs/spdlog`
|
||||||
|
* Arch Linux: `yaourt -S spdlog-git`
|
||||||
|
* vcpkg: `vcpkg install spdlog`
|
||||||
|
|
||||||
|
|
||||||
|
## Platforms
|
||||||
|
* Linux, FreeBSD, OpenBSD, Solaris, AIX
|
||||||
|
* Windows (msvc 2013+, cygwin)
|
||||||
|
* macOS (clang 3.5+)
|
||||||
|
* Android
|
||||||
|
|
||||||
|
## Features
|
||||||
|
* Very fast (see [benchmarks](#benchmarks) below).
|
||||||
|
* Headers only, just copy and use.
|
||||||
|
* Feature rich formatting, using the excellent [fmt](https://github.com/fmtlib/fmt) library.
|
||||||
|
* Fast asynchronous mode (optional)
|
||||||
|
* [Custom](https://github.com/gabime/spdlog/wiki/3.-Custom-formatting) formatting.
|
||||||
|
* Multi/Single threaded loggers.
|
||||||
|
* Various log targets:
|
||||||
|
* Rotating log files.
|
||||||
|
* Daily log files.
|
||||||
|
* Console logging (colors supported).
|
||||||
|
* syslog.
|
||||||
|
* Windows debugger (```OutputDebugString(..)```)
|
||||||
|
* Easily extendable with custom log targets (just implement a single function in the [sink](include/spdlog/sinks/sink.h) interface).
|
||||||
|
* Severity based filtering - threshold levels can be modified in runtime as well as in compile time.
|
||||||
|
* Binary data logging.
|
||||||
|
|
||||||
|
|
||||||
|
## Benchmarks
|
||||||
|
|
||||||
|
Below are some [benchmarks](https://github.com/gabime/spdlog/blob/v1.x/bench/bench.cpp) done in Ubuntu 64 bit, Intel i7-4770 CPU @ 3.40GHz
|
||||||
|
|
||||||
|
#### Synchronous mode
|
||||||
|
```
|
||||||
|
*******************************************************************************
|
||||||
|
Single thread, 1,000,000 iterations
|
||||||
|
*******************************************************************************
|
||||||
|
basic_st... Elapsed: 0.181652 5,505,042/sec
|
||||||
|
rotating_st... Elapsed: 0.181781 5,501,117/sec
|
||||||
|
daily_st... Elapsed: 0.187595 5,330,630/sec
|
||||||
|
null_st... Elapsed: 0.0504704 19,813,602/sec
|
||||||
|
*******************************************************************************
|
||||||
|
10 threads sharing same logger, 1,000,000 iterations
|
||||||
|
*******************************************************************************
|
||||||
|
basic_mt... Elapsed: 0.616035 1,623,284/sec
|
||||||
|
rotating_mt... Elapsed: 0.620344 1,612,008/sec
|
||||||
|
daily_mt... Elapsed: 0.648353 1,542,369/sec
|
||||||
|
null_mt... Elapsed: 0.151972 6,580,166/sec
|
||||||
|
```
|
||||||
|
#### Asynchronous mode
|
||||||
|
```
|
||||||
|
*******************************************************************************
|
||||||
|
10 threads sharing same logger, 1,000,000 iterations
|
||||||
|
*******************************************************************************
|
||||||
|
async... Elapsed: 0.350066 2,856,606/sec
|
||||||
|
async... Elapsed: 0.314865 3,175,960/sec
|
||||||
|
async... Elapsed: 0.349851 2,858,358/sec
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage samples
|
||||||
|
|
||||||
|
#### Basic usage
|
||||||
|
```c++
|
||||||
|
#include "spdlog/spdlog.h"
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
spdlog::info("Welcome to spdlog!");
|
||||||
|
spdlog::error("Some error message with arg: {}", 1);
|
||||||
|
|
||||||
|
spdlog::warn("Easy padding in numbers like {:08d}", 12);
|
||||||
|
spdlog::critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
|
||||||
|
spdlog::info("Support for floats {:03.2f}", 1.23456);
|
||||||
|
spdlog::info("Positional args are {1} {0}..", "too", "supported");
|
||||||
|
spdlog::info("{:<30}", "left aligned");
|
||||||
|
|
||||||
|
spdlog::set_level(spdlog::level::debug); // Set global log level to debug
|
||||||
|
spdlog::debug("This message should be displayed..");
|
||||||
|
|
||||||
|
// change log pattern
|
||||||
|
spdlog::set_pattern("[%H:%M:%S %z] [%n] [%^---%L---%$] [thread %t] %v");
|
||||||
|
|
||||||
|
// Compile time log levels
|
||||||
|
// define SPDLOG_ACTIVE_LEVEL to desired level
|
||||||
|
SPDLOG_TRACE("Some trace message with param {}", {});
|
||||||
|
SPDLOG_DEBUG("Some debug message");
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
#### create stdout/stderr logger object
|
||||||
|
```c++
|
||||||
|
#include "spdlog/spdlog.h"
|
||||||
|
#include "spdlog/sinks/stdout_color_sinks.h"
|
||||||
|
void stdout_example()
|
||||||
|
{
|
||||||
|
// create color multi threaded logger
|
||||||
|
auto console = spdlog::stdout_color_mt("console");
|
||||||
|
auto err_logger = spdlog::stderr_color_mt("stderr");
|
||||||
|
spdlog::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name)");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
---
|
||||||
|
#### Basic file logger
|
||||||
|
```c++
|
||||||
|
#include "spdlog/sinks/basic_file_sink.h"
|
||||||
|
void basic_logfile_example()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
auto my_logger = spdlog::basic_logger_mt("basic_logger", "logs/basic-log.txt");
|
||||||
|
}
|
||||||
|
catch (const spdlog::spdlog_ex &ex)
|
||||||
|
{
|
||||||
|
std::cout << "Log init failed: " << ex.what() << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
---
|
||||||
|
#### Rotating files
|
||||||
|
```c++
|
||||||
|
#include "spdlog/sinks/rotating_file_sink.h"
|
||||||
|
void rotating_example()
|
||||||
|
{
|
||||||
|
// Create a file rotating logger with 5mb size max and 3 rotated files
|
||||||
|
auto rotating_logger = spdlog::rotating_logger_mt("some_logger_name", "logs/rotating.txt", 1048576 * 5, 3);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
#### Daily files
|
||||||
|
```c++
|
||||||
|
|
||||||
|
#include "spdlog/sinks/daily_file_sink.h"
|
||||||
|
void daily_example()
|
||||||
|
{
|
||||||
|
// Create a daily logger - a new file is created every day on 2:30am
|
||||||
|
auto daily_logger = spdlog::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30);
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
#### Cloning loggers
|
||||||
|
```c++
|
||||||
|
// clone a logger and give it new name.
|
||||||
|
// Useful for creating subsystem loggers from some "root" logger
|
||||||
|
void clone_example()
|
||||||
|
{
|
||||||
|
auto network_logger = spdlog::get("root")->clone("network");
|
||||||
|
network_logger->info("Logging network stuff..");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
#### Periodic flush
|
||||||
|
```c++
|
||||||
|
// periodically flush all *registered* loggers every 3 seconds:
|
||||||
|
// warning: only use if all your loggers are thread safe!
|
||||||
|
spdlog::flush_every(std::chrono::seconds(3));
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
#### Binary logging
|
||||||
|
```c++
|
||||||
|
// log binary data as hex.
|
||||||
|
// many types of std::container<char> types can be used.
|
||||||
|
// ranges are supported too.
|
||||||
|
// format flags:
|
||||||
|
// {:X} - print in uppercase.
|
||||||
|
// {:s} - don't separate each byte with space.
|
||||||
|
// {:p} - don't print the position on each line start.
|
||||||
|
// {:n} - don't split the output to lines.
|
||||||
|
|
||||||
|
#include "spdlog/fmt/bin_to_hex.h"
|
||||||
|
|
||||||
|
void binary_example()
|
||||||
|
{
|
||||||
|
auto console = spdlog::get("console");
|
||||||
|
std::array<char, 80> buf;
|
||||||
|
console->info("Binary example: {}", spdlog::to_hex(buf));
|
||||||
|
console->info("Another binary example:{:n}", spdlog::to_hex(std::begin(buf), std::begin(buf) + 10));
|
||||||
|
// more examples:
|
||||||
|
// logger->info("uppercase: {:X}", spdlog::to_hex(buf));
|
||||||
|
// logger->info("uppercase, no delimiters: {:Xs}", spdlog::to_hex(buf));
|
||||||
|
// logger->info("uppercase, no delimiters, no position info: {:Xsp}", spdlog::to_hex(buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
#### Logger with multi sinks - each with different format and log level
|
||||||
|
```c++
|
||||||
|
|
||||||
|
// create logger with 2 targets with different log levels and formats.
|
||||||
|
// the console will show only warnings or errors, while the file will log all.
|
||||||
|
void multi_sink_example()
|
||||||
|
{
|
||||||
|
auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
|
||||||
|
console_sink->set_level(spdlog::level::warn);
|
||||||
|
console_sink->set_pattern("[multi_sink_example] [%^%l%$] %v");
|
||||||
|
|
||||||
|
auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("logs/multisink.txt", true);
|
||||||
|
file_sink->set_level(spdlog::level::trace);
|
||||||
|
|
||||||
|
spdlog::logger logger("multi_sink", {console_sink, file_sink});
|
||||||
|
logger.set_level(spdlog::level::debug);
|
||||||
|
logger.warn("this should appear in both console and file");
|
||||||
|
logger.info("this message should not appear in the console, only in the file");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
#### Asynchronous logging
|
||||||
|
```c++
|
||||||
|
#include "spdlog/async.h"
|
||||||
|
#include "spdlog/sinks/basic_file_sink.h"
|
||||||
|
void async_example()
|
||||||
|
{
|
||||||
|
// default thread pool settings can be modified *before* creating the async logger:
|
||||||
|
// spdlog::init_thread_pool(8192, 1); // queue with 8k items and 1 backing thread.
|
||||||
|
auto async_file = spdlog::basic_logger_mt<spdlog::async_factory>("async_file_logger", "logs/async_log.txt");
|
||||||
|
// alternatively:
|
||||||
|
// auto async_file = spdlog::create_async<spdlog::sinks::basic_file_sink_mt>("async_file_logger", "logs/async_log.txt");
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
#### Asynchronous logger with multi sinks
|
||||||
|
```c++
|
||||||
|
#include "spdlog/sinks/stdout_color_sinks.h"
|
||||||
|
#include "spdlog/sinks/rotating_file_sink.h"
|
||||||
|
|
||||||
|
void multi_sink_example2()
|
||||||
|
{
|
||||||
|
spdlog::init_thread_pool(8192, 1);
|
||||||
|
auto stdout_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt >();
|
||||||
|
auto rotating_sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>("mylog.txt", 1024*1024*10, 3);
|
||||||
|
std::vector<spdlog::sink_ptr> sinks {stdout_sink, rotating_sink};
|
||||||
|
auto logger = std::make_shared<spdlog::async_logger>("loggername", sinks.begin(), sinks.end(), spdlog::thread_pool(), spdlog::async_overflow_policy::block);
|
||||||
|
spdlog::register_logger(logger);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
#### User defined types
|
||||||
|
```c++
|
||||||
|
// user defined types logging by implementing operator<<
|
||||||
|
#include "spdlog/fmt/ostr.h" // must be included
|
||||||
|
struct my_type
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
template<typename OStream>
|
||||||
|
friend OStream &operator<<(OStream &os, const my_type &c)
|
||||||
|
{
|
||||||
|
return os << "[my_type i=" << c.i << "]";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void user_defined_example()
|
||||||
|
{
|
||||||
|
spdlog::get("console")->info("user defined type: {}", my_type{14});
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
---
|
||||||
|
#### Custom error handler
|
||||||
|
```c++
|
||||||
|
void err_handler_example()
|
||||||
|
{
|
||||||
|
// can be set globally or per logger(logger->set_error_handler(..))
|
||||||
|
spdlog::set_error_handler([](const std::string &msg) { spdlog::get("console")->error("*** LOGGER ERROR ***: {}", msg); });
|
||||||
|
spdlog::get("console")->info("some invalid message to trigger an error {}{}{}{}", 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
---
|
||||||
|
#### syslog
|
||||||
|
```c++
|
||||||
|
#include "spdlog/sinks/syslog_sink.h"
|
||||||
|
void syslog_example()
|
||||||
|
{
|
||||||
|
std::string ident = "spdlog-example";
|
||||||
|
auto syslog_logger = spdlog::syslog_logger_mt("syslog", ident, LOG_PID);
|
||||||
|
syslog_logger->warn("This is warning that will end up in syslog.");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
---
|
||||||
|
#### Android example
|
||||||
|
```c++
|
||||||
|
#include "spdlog/sinks/android_sink.h"
|
||||||
|
void android_example()
|
||||||
|
{
|
||||||
|
std::string tag = "spdlog-android";
|
||||||
|
auto android_logger = spdlog::android_logger("android", tag);
|
||||||
|
android_logger->critical("Use \"adb shell logcat\" to view this message.");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
Documentation can be found in the [wiki](https://github.com/gabime/spdlog/wiki/1.-QuickStart) pages.
|
34
third_party/spdlog/appveyor.yml
vendored
Normal file
34
third_party/spdlog/appveyor.yml
vendored
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
version: 1.0.{build}
|
||||||
|
image: Visual Studio 2015
|
||||||
|
environment:
|
||||||
|
matrix:
|
||||||
|
- GENERATOR: '"MinGW Makefiles"'
|
||||||
|
BUILD_TYPE: Debug
|
||||||
|
- GENERATOR: '"MinGW Makefiles"'
|
||||||
|
BUILD_TYPE: Release
|
||||||
|
- GENERATOR: '"Visual Studio 14 2015"'
|
||||||
|
BUILD_TYPE: Debug
|
||||||
|
- GENERATOR: '"Visual Studio 14 2015"'
|
||||||
|
BUILD_TYPE: Release
|
||||||
|
- GENERATOR: '"Visual Studio 14 2015 Win64"'
|
||||||
|
BUILD_TYPE: Debug
|
||||||
|
- GENERATOR: '"Visual Studio 14 2015 Win64"'
|
||||||
|
BUILD_TYPE: Release
|
||||||
|
build_script:
|
||||||
|
- cmd: >-
|
||||||
|
set
|
||||||
|
|
||||||
|
mkdir build
|
||||||
|
|
||||||
|
cd build
|
||||||
|
|
||||||
|
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_BUILD_BENCH=OFF
|
||||||
|
|
||||||
|
cmake --build . --config %BUILD_TYPE%
|
||||||
|
|
||||||
|
test_script:
|
||||||
|
- ctest -VV -C "%BUILD_TYPE%"
|
48
third_party/spdlog/bench/CMakeLists.txt
vendored
Normal file
48
third_party/spdlog/bench/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
# *************************************************************************/
|
||||||
|
# * Copyright (c) 2015 Ruslan Baratov. */
|
||||||
|
# * */
|
||||||
|
# * Permission is hereby granted, free of charge, to any person obtaining */
|
||||||
|
# * a copy of this software and associated documentation files (the */
|
||||||
|
# * "Software"), to deal in the Software without restriction, including */
|
||||||
|
# * without limitation the rights to use, copy, modify, merge, publish, */
|
||||||
|
# * distribute, sublicense, and/or sell copies of the Software, and to */
|
||||||
|
# * permit persons to whom the Software is furnished to do so, subject to */
|
||||||
|
# * the following conditions: */
|
||||||
|
# * */
|
||||||
|
# * The above copyright notice and this permission notice shall be */
|
||||||
|
# * included in all copies or substantial portions of the Software. */
|
||||||
|
# * */
|
||||||
|
# * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||||
|
# * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||||
|
# * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||||
|
# * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||||
|
# * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||||
|
# * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||||
|
# * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||||
|
# *************************************************************************/
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 3.1)
|
||||||
|
project(SpdlogBench CXX)
|
||||||
|
|
||||||
|
if(NOT TARGET spdlog)
|
||||||
|
# Stand-alone build
|
||||||
|
find_package(spdlog CONFIG REQUIRED)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
find_package(Threads REQUIRED)
|
||||||
|
find_package(benchmark CONFIG REQUIRED)
|
||||||
|
|
||||||
|
add_executable(bench bench.cpp)
|
||||||
|
target_link_libraries(bench PRIVATE spdlog::spdlog Threads::Threads)
|
||||||
|
|
||||||
|
add_executable(async_bench async_bench.cpp)
|
||||||
|
target_link_libraries(async_bench PRIVATE spdlog::spdlog Threads::Threads)
|
||||||
|
|
||||||
|
add_executable(latency latency.cpp)
|
||||||
|
target_link_libraries(latency PRIVATE benchmark::benchmark spdlog::spdlog Threads::Threads)
|
||||||
|
|
||||||
|
|
||||||
|
add_executable(formatter-bench formatter-bench.cpp)
|
||||||
|
target_link_libraries(formatter-bench PRIVATE benchmark::benchmark spdlog::spdlog Threads::Threads)
|
||||||
|
|
||||||
|
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/logs")
|
141
third_party/spdlog/bench/async_bench.cpp
vendored
Normal file
141
third_party/spdlog/bench/async_bench.cpp
vendored
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
//
|
||||||
|
// Copyright(c) 2015 Gabi Melman.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
|
||||||
|
//
|
||||||
|
// bench.cpp : spdlog benchmarks
|
||||||
|
//
|
||||||
|
#include "spdlog/spdlog.h"
|
||||||
|
#include "spdlog/async.h"
|
||||||
|
#include "spdlog/sinks/basic_file_sink.h"
|
||||||
|
#include "spdlog/sinks/stdout_color_sinks.h"
|
||||||
|
|
||||||
|
#include "utils.h"
|
||||||
|
#include <atomic>
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace std::chrono;
|
||||||
|
using namespace spdlog;
|
||||||
|
using namespace spdlog::sinks;
|
||||||
|
using namespace utils;
|
||||||
|
|
||||||
|
void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count);
|
||||||
|
|
||||||
|
int count_lines(const char *filename)
|
||||||
|
{
|
||||||
|
int counter = 0;
|
||||||
|
auto *infile = fopen(filename, "r");
|
||||||
|
int ch;
|
||||||
|
while (EOF != (ch = getc(infile)))
|
||||||
|
{
|
||||||
|
if ('\n' == ch)
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
fclose(infile);
|
||||||
|
|
||||||
|
return counter;
|
||||||
|
}
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
|
||||||
|
int howmany = 1000000;
|
||||||
|
int queue_size = howmany + 2;
|
||||||
|
int threads = 10;
|
||||||
|
int iters = 3;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (argc == 1)
|
||||||
|
{
|
||||||
|
spdlog::set_pattern("%v");
|
||||||
|
spdlog::info("Usage: {} <message_count> <threads> <q_size> <iterations>", argv[0]);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argc > 1)
|
||||||
|
howmany = atoi(argv[1]);
|
||||||
|
if (argc > 2)
|
||||||
|
threads = atoi(argv[2]);
|
||||||
|
if (argc > 3)
|
||||||
|
queue_size = atoi(argv[3]);
|
||||||
|
|
||||||
|
if (argc > 4)
|
||||||
|
iters = atoi(argv[4]);
|
||||||
|
|
||||||
|
spdlog::info("-------------------------------------------------");
|
||||||
|
spdlog::info("Messages: {:14n}", howmany);
|
||||||
|
spdlog::info("Threads : {:14n}", threads);
|
||||||
|
spdlog::info("Queue : {:14n}", queue_size);
|
||||||
|
spdlog::info("Iters : {:>14n}", iters);
|
||||||
|
spdlog::info("-------------------------------------------------");
|
||||||
|
|
||||||
|
const char *filename = "logs/basic_async.log";
|
||||||
|
|
||||||
|
for (int i = 0; i < iters; i++)
|
||||||
|
{
|
||||||
|
auto tp = std::make_shared<details::thread_pool>(queue_size, 1);
|
||||||
|
auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(filename, true);
|
||||||
|
auto logger = std::make_shared<async_logger>("async_logger", std::move(file_sink), std::move(tp), async_overflow_policy::block);
|
||||||
|
bench_mt(howmany, std::move(logger), threads);
|
||||||
|
auto count = count_lines(filename);
|
||||||
|
|
||||||
|
if (count != howmany)
|
||||||
|
{
|
||||||
|
spdlog::error("Test failed. {} has {:n} lines instead of {:n}", filename, count, howmany);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
spdlog::info("Line count OK ({:n})\n", count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spdlog::shutdown();
|
||||||
|
}
|
||||||
|
catch (std::exception &ex)
|
||||||
|
{
|
||||||
|
std::cerr << "Error: " << ex.what() << std::endl;
|
||||||
|
perror("Last error");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void thread_fun(std::shared_ptr<spdlog::logger> logger, int howmany)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < howmany; i++)
|
||||||
|
{
|
||||||
|
logger->info("Hello logger: msg number {}", i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void bench_mt(int howmany, std::shared_ptr<spdlog::logger> logger, int thread_count)
|
||||||
|
{
|
||||||
|
using std::chrono::high_resolution_clock;
|
||||||
|
vector<thread> threads;
|
||||||
|
auto start = high_resolution_clock::now();
|
||||||
|
|
||||||
|
int msgs_per_thread = howmany / thread_count;
|
||||||
|
int msgs_per_thread_mod = howmany % thread_count;
|
||||||
|
for (int t = 0; t < thread_count; ++t)
|
||||||
|
{
|
||||||
|
if (t == 0 && msgs_per_thread_mod)
|
||||||
|
threads.push_back(std::thread(thread_fun, logger, msgs_per_thread + msgs_per_thread_mod));
|
||||||
|
else
|
||||||
|
threads.push_back(std::thread(thread_fun, logger, msgs_per_thread));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &t : threads)
|
||||||
|
{
|
||||||
|
t.join();
|
||||||
|
};
|
||||||
|
|
||||||
|
auto delta = high_resolution_clock::now() - start;
|
||||||
|
auto delta_d = duration_cast<duration<double>>(delta).count();
|
||||||
|
spdlog::info("Elapsed: {} secs\t {:n}/sec", delta_d, int(howmany / delta_d));
|
||||||
|
}
|
199
third_party/spdlog/bench/bench.cpp
vendored
Normal file
199
third_party/spdlog/bench/bench.cpp
vendored
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
//
|
||||||
|
// Copyright(c) 2015 Gabi Melman.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
|
||||||
|
//
|
||||||
|
// bench.cpp : spdlog benchmarks
|
||||||
|
//
|
||||||
|
#include "spdlog/spdlog.h"
|
||||||
|
#include "spdlog/async.h"
|
||||||
|
#include "spdlog/sinks/basic_file_sink.h"
|
||||||
|
#include "spdlog/sinks/daily_file_sink.h"
|
||||||
|
#include "spdlog/sinks/null_sink.h"
|
||||||
|
#include "spdlog/sinks/rotating_file_sink.h"
|
||||||
|
|
||||||
|
#include "utils.h"
|
||||||
|
#include <atomic>
|
||||||
|
#include <cstdlib> // EXIT_FAILURE
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace std::chrono;
|
||||||
|
using namespace spdlog;
|
||||||
|
using namespace spdlog::sinks;
|
||||||
|
using namespace utils;
|
||||||
|
|
||||||
|
void bench(int howmany, std::shared_ptr<spdlog::logger> log);
|
||||||
|
void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count);
|
||||||
|
void bench_default_api(int howmany, std::shared_ptr<spdlog::logger> log);
|
||||||
|
void bench_c_string(int howmany, std::shared_ptr<spdlog::logger> log);
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
|
||||||
|
spdlog::default_logger()->set_pattern("[%^%l%$] %v");
|
||||||
|
int howmany = 1000000;
|
||||||
|
int queue_size = howmany + 2;
|
||||||
|
int threads = 10;
|
||||||
|
size_t file_size = 30 * 1024 * 1024;
|
||||||
|
size_t rotating_files = 5;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
|
||||||
|
if (argc > 1)
|
||||||
|
howmany = atoi(argv[1]);
|
||||||
|
if (argc > 2)
|
||||||
|
threads = atoi(argv[2]);
|
||||||
|
if (argc > 3)
|
||||||
|
queue_size = atoi(argv[3]);
|
||||||
|
|
||||||
|
spdlog::info("**************************************************************");
|
||||||
|
spdlog::info("Single thread, {:n} iterations", howmany);
|
||||||
|
spdlog::info("**************************************************************");
|
||||||
|
|
||||||
|
auto basic_st = spdlog::basic_logger_st("basic_st", "logs/basic_st.log", true);
|
||||||
|
bench(howmany, std::move(basic_st));
|
||||||
|
|
||||||
|
basic_st.reset();
|
||||||
|
auto rotating_st = spdlog::rotating_logger_st("rotating_st", "logs/rotating_st.log", file_size, rotating_files);
|
||||||
|
bench(howmany, std::move(rotating_st));
|
||||||
|
|
||||||
|
auto daily_st = spdlog::daily_logger_st("daily_st", "logs/daily_st.log");
|
||||||
|
bench(howmany, std::move(daily_st));
|
||||||
|
|
||||||
|
bench(howmany, spdlog::create<null_sink_st>("null_st"));
|
||||||
|
|
||||||
|
spdlog::info("**************************************************************");
|
||||||
|
spdlog::info("C-string (400 bytes). Single thread, {:n} iterations", howmany);
|
||||||
|
spdlog::info("**************************************************************");
|
||||||
|
|
||||||
|
basic_st = spdlog::basic_logger_st("basic_st", "logs/basic_cs.log", true);
|
||||||
|
bench_c_string(howmany, std::move(basic_st));
|
||||||
|
|
||||||
|
rotating_st = spdlog::rotating_logger_st("rotating_st", "logs/rotating_cs.log", file_size, rotating_files);
|
||||||
|
bench_c_string(howmany, std::move(rotating_st));
|
||||||
|
|
||||||
|
daily_st = spdlog::daily_logger_st("daily_st", "logs/daily_cs.log");
|
||||||
|
bench_c_string(howmany, std::move(daily_st));
|
||||||
|
|
||||||
|
bench_c_string(howmany, spdlog::create<null_sink_st>("null_st"));
|
||||||
|
|
||||||
|
spdlog::info("**************************************************************");
|
||||||
|
spdlog::info("{:n} threads sharing same logger, {:n} iterations", threads, howmany);
|
||||||
|
spdlog::info("**************************************************************");
|
||||||
|
|
||||||
|
auto basic_mt = spdlog::basic_logger_mt("basic_mt", "logs/basic_mt.log", true);
|
||||||
|
bench_mt(howmany, std::move(basic_mt), threads);
|
||||||
|
|
||||||
|
auto rotating_mt = spdlog::rotating_logger_mt("rotating_mt", "logs/rotating_mt.log", file_size, rotating_files);
|
||||||
|
bench_mt(howmany, std::move(rotating_mt), threads);
|
||||||
|
|
||||||
|
auto daily_mt = spdlog::daily_logger_mt("daily_mt", "logs/daily_mt.log");
|
||||||
|
bench_mt(howmany, std::move(daily_mt), threads);
|
||||||
|
bench_mt(howmany, spdlog::create<null_sink_mt>("null_mt"), threads);
|
||||||
|
|
||||||
|
spdlog::info("**************************************************************");
|
||||||
|
spdlog::info("Asyncronous.. {:n} threads sharing same logger, {:n} iterations", threads, howmany);
|
||||||
|
spdlog::info("**************************************************************");
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; ++i)
|
||||||
|
{
|
||||||
|
spdlog::init_thread_pool(static_cast<size_t>(queue_size), 1);
|
||||||
|
auto as = spdlog::basic_logger_mt<spdlog::async_factory>("async", "logs/basic_async.log", true);
|
||||||
|
bench_mt(howmany, std::move(as), threads);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (std::exception &ex)
|
||||||
|
{
|
||||||
|
spdlog::error(ex.what());
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bench(int howmany, std::shared_ptr<spdlog::logger> log)
|
||||||
|
{
|
||||||
|
using std::chrono::high_resolution_clock;
|
||||||
|
auto start = high_resolution_clock::now();
|
||||||
|
for (auto i = 0; i < howmany; ++i)
|
||||||
|
{
|
||||||
|
log->info("Hello logger: msg number {}", i);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto delta = high_resolution_clock::now() - start;
|
||||||
|
auto delta_d = duration_cast<duration<double>>(delta).count();
|
||||||
|
|
||||||
|
spdlog::info("{:<16} Elapsed: {:0.2f} secs {:>16n}/sec", log->name(), delta_d, int(howmany / delta_d));
|
||||||
|
spdlog::drop(log->name());
|
||||||
|
}
|
||||||
|
|
||||||
|
void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count)
|
||||||
|
{
|
||||||
|
using std::chrono::high_resolution_clock;
|
||||||
|
vector<thread> threads;
|
||||||
|
auto start = high_resolution_clock::now();
|
||||||
|
for (int t = 0; t < thread_count; ++t)
|
||||||
|
{
|
||||||
|
threads.push_back(std::thread([&]() {
|
||||||
|
for (int j = 0; j < howmany / thread_count; j++)
|
||||||
|
{
|
||||||
|
log->info("Hello logger: msg number {}", j);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &t : threads)
|
||||||
|
{
|
||||||
|
t.join();
|
||||||
|
};
|
||||||
|
|
||||||
|
auto delta = high_resolution_clock::now() - start;
|
||||||
|
auto delta_d = duration_cast<duration<double>>(delta).count();
|
||||||
|
spdlog::info("{:<16} Elapsed: {:0.2f} secs {:>16n}/sec", log->name(), delta_d, int(howmany / delta_d));
|
||||||
|
spdlog::drop(log->name());
|
||||||
|
}
|
||||||
|
|
||||||
|
void bench_default_api(int howmany, std::shared_ptr<spdlog::logger> log)
|
||||||
|
{
|
||||||
|
using std::chrono::high_resolution_clock;
|
||||||
|
auto orig_default = spdlog::default_logger();
|
||||||
|
spdlog::set_default_logger(log);
|
||||||
|
auto start = high_resolution_clock::now();
|
||||||
|
for (auto i = 0; i < howmany; ++i)
|
||||||
|
{
|
||||||
|
spdlog::info("Hello logger: msg number {}", i);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto delta = high_resolution_clock::now() - start;
|
||||||
|
auto delta_d = duration_cast<duration<double>>(delta).count();
|
||||||
|
spdlog::drop(log->name());
|
||||||
|
spdlog::set_default_logger(std::move(orig_default));
|
||||||
|
spdlog::info("{:<16} Elapsed: {:0.2f} secs {:>16n}/sec", log->name(), delta_d, int(howmany / delta_d));
|
||||||
|
}
|
||||||
|
|
||||||
|
void bench_c_string(int howmany, std::shared_ptr<spdlog::logger> log)
|
||||||
|
{
|
||||||
|
const char *msg = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum pharetra metus cursus "
|
||||||
|
"lacus placerat congue. Nulla egestas, mauris a tincidunt tempus, enim lectus volutpat mi, eu consequat sem "
|
||||||
|
"libero nec massa. In dapibus ipsum a diam rhoncus gravida. Etiam non dapibus eros. Donec fringilla dui sed "
|
||||||
|
"augue pretium, nec scelerisque est maximus. Nullam convallis, sem nec blandit maximus, nisi turpis ornare "
|
||||||
|
"nisl, sit amet volutpat neque massa eu odio. Maecenas malesuada quam ex, posuere congue nibh turpis duis.";
|
||||||
|
using std::chrono::high_resolution_clock;
|
||||||
|
auto orig_default = spdlog::default_logger();
|
||||||
|
spdlog::set_default_logger(log);
|
||||||
|
auto start = high_resolution_clock::now();
|
||||||
|
for (auto i = 0; i < howmany; ++i)
|
||||||
|
{
|
||||||
|
spdlog::log(level::info, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto delta = high_resolution_clock::now() - start;
|
||||||
|
auto delta_d = duration_cast<duration<double>>(delta).count();
|
||||||
|
spdlog::drop(log->name());
|
||||||
|
spdlog::set_default_logger(std::move(orig_default));
|
||||||
|
spdlog::info("{:<16} Elapsed: {:0.2f} secs {:>16n}/sec", log->name(), delta_d, int(howmany / delta_d));
|
||||||
|
}
|
92
third_party/spdlog/bench/formatter-bench.cpp
vendored
Normal file
92
third_party/spdlog/bench/formatter-bench.cpp
vendored
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
//
|
||||||
|
// Copyright(c) 2018 Gabi Melman.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "benchmark/benchmark.h"
|
||||||
|
|
||||||
|
#include "spdlog/spdlog.h"
|
||||||
|
#include "spdlog/details/pattern_formatter.h"
|
||||||
|
|
||||||
|
void bench_scoped_pad(benchmark::State &state, size_t wrapped_size, spdlog::details::padding_info padinfo)
|
||||||
|
{
|
||||||
|
fmt::memory_buffer dest;
|
||||||
|
for (auto _ : state)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
spdlog::details::scoped_pad p(wrapped_size, padinfo, dest);
|
||||||
|
benchmark::DoNotOptimize(p);
|
||||||
|
dest.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void bench_formatter(benchmark::State &state, std::string pattern)
|
||||||
|
{
|
||||||
|
auto formatter = spdlog::details::make_unique<spdlog::pattern_formatter>(pattern);
|
||||||
|
fmt::memory_buffer dest;
|
||||||
|
std::string logger_name = "logger-name";
|
||||||
|
const char *text = "Hello. This is some message with length of 80 ";
|
||||||
|
|
||||||
|
spdlog::details::log_msg msg(&logger_name, spdlog::level::info, text);
|
||||||
|
|
||||||
|
for (auto _ : state)
|
||||||
|
{
|
||||||
|
dest.clear();
|
||||||
|
formatter->format(msg, dest);
|
||||||
|
benchmark::DoNotOptimize(dest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void bench_formatters()
|
||||||
|
{
|
||||||
|
// basic patterns(single flag)
|
||||||
|
std::string all_flags = "+vtPnlLaAbBcCYDmdHIMSefFprRTXzEi%";
|
||||||
|
std::vector<std::string> basic_patterns;
|
||||||
|
for (auto &flag : all_flags)
|
||||||
|
{
|
||||||
|
auto pattern = std::string("%") + flag;
|
||||||
|
benchmark::RegisterBenchmark(pattern.c_str(), bench_formatter, pattern);
|
||||||
|
|
||||||
|
// pattern = std::string("%16") + flag;
|
||||||
|
// benchmark::RegisterBenchmark(pattern.c_str(), bench_formatter, pattern);
|
||||||
|
//
|
||||||
|
// // bench center padding
|
||||||
|
// pattern = std::string("%=16") + flag;
|
||||||
|
// benchmark::RegisterBenchmark(pattern.c_str(), bench_formatter, pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
// complex patterns
|
||||||
|
std::vector<std::string> patterns = {
|
||||||
|
"[%D %X] [%l] [%n] %v",
|
||||||
|
"[%Y-%m-%d %H:%M:%S.%e] [%l] [%n] %v",
|
||||||
|
"[%Y-%m-%d %H:%M:%S.%e] [%l] [%n] [%t] %v",
|
||||||
|
};
|
||||||
|
for (auto &pattern : patterns)
|
||||||
|
{
|
||||||
|
benchmark::RegisterBenchmark(pattern.c_str(), bench_formatter, pattern)->Iterations(2500000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
|
||||||
|
spdlog::set_pattern("[%^%l%$] %v");
|
||||||
|
if (argc != 2)
|
||||||
|
{
|
||||||
|
spdlog::error("Usage: {} <pattern> (or \"all\" to bench all)", argv[0]);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string pattern = argv[1];
|
||||||
|
if (pattern == "all")
|
||||||
|
{
|
||||||
|
bench_formatters();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
benchmark::RegisterBenchmark(pattern.c_str(), bench_formatter, pattern);
|
||||||
|
}
|
||||||
|
benchmark::Initialize(&argc, argv);
|
||||||
|
benchmark::RunSpecifiedBenchmarks();
|
||||||
|
}
|
143
third_party/spdlog/bench/latency.cpp
vendored
Normal file
143
third_party/spdlog/bench/latency.cpp
vendored
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
//
|
||||||
|
// Copyright(c) 2018 Gabi Melman.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
|
||||||
|
//
|
||||||
|
// latency.cpp : spdlog latency benchmarks
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "benchmark/benchmark.h"
|
||||||
|
|
||||||
|
#include "spdlog/spdlog.h"
|
||||||
|
#include "spdlog/async.h"
|
||||||
|
#include "spdlog/sinks/basic_file_sink.h"
|
||||||
|
#include "spdlog/sinks/daily_file_sink.h"
|
||||||
|
#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 "
|
||||||
|
"lacus placerat congue. Nulla egestas, mauris a tincidunt tempus, enim lectus volutpat mi, eu consequat sem "
|
||||||
|
"libero nec massa. In dapibus ipsum a diam rhoncus gravida. Etiam non dapibus eros. Donec fringilla dui sed "
|
||||||
|
"augue pretium, nec scelerisque est maximus. Nullam convallis, sem nec blandit maximus, nisi turpis ornare "
|
||||||
|
"nisl, sit amet volutpat neque massa eu odio. Maecenas malesuada quam ex, posuere congue nibh turpis duis.";
|
||||||
|
|
||||||
|
for (auto _ : state)
|
||||||
|
{
|
||||||
|
logger->info(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void bench_logger(benchmark::State &state, std::shared_ptr<spdlog::logger> logger)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
for (auto _ : state)
|
||||||
|
{
|
||||||
|
logger->info("Hello logger: msg number {}...............", ++i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void bench_disabled_macro(benchmark::State &state, std::shared_ptr<spdlog::logger> logger)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
benchmark::DoNotOptimize(i); // prevent unused warnings
|
||||||
|
benchmark::DoNotOptimize(logger); // prevent unused warnings
|
||||||
|
for (auto _ : state)
|
||||||
|
{
|
||||||
|
SPDLOG_LOGGER_DEBUG(logger, "Hello logger: msg number {}...............", i++);
|
||||||
|
SPDLOG_DEBUG("Hello logger: msg number {}...............", i++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
|
||||||
|
using spdlog::sinks::basic_file_sink_mt;
|
||||||
|
using spdlog::sinks::basic_file_sink_st;
|
||||||
|
using spdlog::sinks::null_sink_mt;
|
||||||
|
using spdlog::sinks::null_sink_st;
|
||||||
|
|
||||||
|
size_t file_size = 30 * 1024 * 1024;
|
||||||
|
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);
|
||||||
|
benchmark::RegisterBenchmark("disabled-at-compile-time", bench_disabled_macro, disabled_logger);
|
||||||
|
benchmark::RegisterBenchmark("disabled-at-runtime", bench_logger, disabled_logger);
|
||||||
|
|
||||||
|
auto null_logger_st = std::make_shared<spdlog::logger>("bench", std::make_shared<null_sink_st>());
|
||||||
|
benchmark::RegisterBenchmark("null_sink_st (500_bytes c_str)", bench_c_string, std::move(null_logger_st));
|
||||||
|
benchmark::RegisterBenchmark("null_sink_st", bench_logger, null_logger_st);
|
||||||
|
|
||||||
|
// basic_st
|
||||||
|
auto basic_st = spdlog::basic_logger_st("basic_st", "latency_logs/basic_st.log", true);
|
||||||
|
benchmark::RegisterBenchmark("basic_st", bench_logger, std::move(basic_st))->UseRealTime();
|
||||||
|
spdlog::drop("basic_st");
|
||||||
|
|
||||||
|
// rotating st
|
||||||
|
auto rotating_st = spdlog::rotating_logger_st("rotating_st", "latency_logs/rotating_st.log", file_size, rotating_files);
|
||||||
|
benchmark::RegisterBenchmark("rotating_st", bench_logger, std::move(rotating_st))->UseRealTime();
|
||||||
|
spdlog::drop("rotating_st");
|
||||||
|
|
||||||
|
// daily st
|
||||||
|
auto daily_st = spdlog::daily_logger_mt("daily_st", "latency_logs/daily_st.log");
|
||||||
|
benchmark::RegisterBenchmark("daily_st", bench_logger, std::move(daily_st))->UseRealTime();
|
||||||
|
spdlog::drop("daily_st");
|
||||||
|
|
||||||
|
// //
|
||||||
|
// // Multi threaded bench, 10 loggers using same logger concurrently
|
||||||
|
// //
|
||||||
|
auto null_logger_mt = std::make_shared<spdlog::logger>("bench", std::make_shared<null_sink_mt>());
|
||||||
|
benchmark::RegisterBenchmark("null_sink_mt", bench_logger, null_logger_mt)->Threads(n_threads)->UseRealTime();
|
||||||
|
|
||||||
|
// basic_mt
|
||||||
|
auto basic_mt = spdlog::basic_logger_mt("basic_mt", "latency_logs/basic_mt.log", true);
|
||||||
|
benchmark::RegisterBenchmark("basic_mt", bench_logger, std::move(basic_mt))->Threads(n_threads)->UseRealTime();
|
||||||
|
spdlog::drop("basic_mt");
|
||||||
|
|
||||||
|
// rotating mt
|
||||||
|
auto rotating_mt = spdlog::rotating_logger_mt("rotating_mt", "latency_logs/rotating_mt.log", file_size, rotating_files);
|
||||||
|
benchmark::RegisterBenchmark("rotating_mt", bench_logger, std::move(rotating_mt))->Threads(n_threads)->UseRealTime();
|
||||||
|
spdlog::drop("rotating_mt");
|
||||||
|
|
||||||
|
// daily mt
|
||||||
|
auto daily_mt = spdlog::daily_logger_mt("daily_mt", "latency_logs/daily_mt.log");
|
||||||
|
benchmark::RegisterBenchmark("daily_mt", bench_logger, std::move(daily_mt))->Threads(n_threads)->UseRealTime();
|
||||||
|
spdlog::drop("daily_mt");
|
||||||
|
|
||||||
|
// async
|
||||||
|
auto queue_size = 1024 * 1024 * 3;
|
||||||
|
auto tp = std::make_shared<spdlog::details::thread_pool>(queue_size, 1);
|
||||||
|
auto async_logger = std::make_shared<spdlog::async_logger>(
|
||||||
|
"async_logger", std::make_shared<null_sink_mt>(), std::move(tp), spdlog::async_overflow_policy::overrun_oldest);
|
||||||
|
benchmark::RegisterBenchmark("async_logger", bench_logger, async_logger)->Threads(n_threads)->UseRealTime();
|
||||||
|
|
||||||
|
benchmark::Initialize(&argc, argv);
|
||||||
|
benchmark::RunSpecifiedBenchmarks();
|
||||||
|
}
|
4
third_party/spdlog/bench/logs/.gitignore
vendored
Normal file
4
third_party/spdlog/bench/logs/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# Ignore everything in this directory
|
||||||
|
*
|
||||||
|
# Except this file
|
||||||
|
!.gitignore
|
19
third_party/spdlog/bench/mem
vendored
Executable file
19
third_party/spdlog/bench/mem
vendored
Executable file
@ -0,0 +1,19 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
if [ $# -lt 1 ]; then
|
||||||
|
echo "usage: $0 <program>"
|
||||||
|
fi
|
||||||
|
|
||||||
|
PROG=$1
|
||||||
|
|
||||||
|
if [ ! -x "$PROG" ]; then
|
||||||
|
echo $PROG not found or not executable.
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
$* &
|
||||||
|
PID=$!
|
||||||
|
|
||||||
|
while `kill -0 $PID 2>/dev/null`; do
|
||||||
|
ps -eo size,pid,user,pcpu,command --sort -size | awk '{ line=1 ; hr=$1/1024 ; printf("%13.2f Mb ",hr); } { for ( x=4 ; x<=NF ; x++ ) { printf("%s ",$x) } print "" }' | grep -v grep | grep -v $0 | grep $PROG
|
||||||
|
done
|
34
third_party/spdlog/bench/utils.h
vendored
Normal file
34
third_party/spdlog/bench/utils.h
vendored
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
//
|
||||||
|
// Copyright(c) 2015 Gabi Melman.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <iomanip>
|
||||||
|
#include <locale>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
namespace utils {
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline std::string format(const T &value)
|
||||||
|
{
|
||||||
|
static std::locale loc("");
|
||||||
|
std::stringstream ss;
|
||||||
|
ss.imbue(loc);
|
||||||
|
ss << value;
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
inline std::string format(const double &value)
|
||||||
|
{
|
||||||
|
static std::locale loc("");
|
||||||
|
std::stringstream ss;
|
||||||
|
ss.imbue(loc);
|
||||||
|
ss << std::fixed << std::setprecision(1) << value;
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace utils
|
2
third_party/spdlog/clang_tidy.sh
vendored
Executable file
2
third_party/spdlog/clang_tidy.sh
vendored
Executable file
@ -0,0 +1,2 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
clang-tidy example/example.cpp -- -I ./include
|
31
third_party/spdlog/cmake/Config.cmake.in
vendored
Normal file
31
third_party/spdlog/cmake/Config.cmake.in
vendored
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
# *************************************************************************/
|
||||||
|
# * Copyright (c) 2015 Ruslan Baratov. */
|
||||||
|
# * */
|
||||||
|
# * Permission is hereby granted, free of charge, to any person obtaining */
|
||||||
|
# * a copy of this software and associated documentation files (the */
|
||||||
|
# * "Software"), to deal in the Software without restriction, including */
|
||||||
|
# * without limitation the rights to use, copy, modify, merge, publish, */
|
||||||
|
# * distribute, sublicense, and/or sell copies of the Software, and to */
|
||||||
|
# * permit persons to whom the Software is furnished to do so, subject to */
|
||||||
|
# * the following conditions: */
|
||||||
|
# * */
|
||||||
|
# * The above copyright notice and this permission notice shall be */
|
||||||
|
# * included in all copies or substantial portions of the Software. */
|
||||||
|
# * */
|
||||||
|
# * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||||
|
# * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||||
|
# * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||||
|
# * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||||
|
# * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||||
|
# * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||||
|
# * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||||
|
# *************************************************************************/
|
||||||
|
|
||||||
|
set(SPDLOG_FMT_EXTERNAL @SPDLOG_FMT_EXTERNAL@)
|
||||||
|
|
||||||
|
include("${CMAKE_CURRENT_LIST_DIR}/@targets_export_name@.cmake")
|
||||||
|
|
||||||
|
if(SPDLOG_FMT_EXTERNAL)
|
||||||
|
include(CMakeFindDependencyMacro)
|
||||||
|
find_dependency(fmt CONFIG)
|
||||||
|
endif()
|
21
third_party/spdlog/cmake/sanitizers.cmake
vendored
Normal file
21
third_party/spdlog/cmake/sanitizers.cmake
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
if(SPDLOG_SANITIZE_THREAD AND SPDLOG_SANITIZE_ADDRESS)
|
||||||
|
message(FATAL_ERROR "AddressSanitizer is not compatible with ThreadSanitizer.")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(SPDLOG_SANITIZE_ADDRESS)
|
||||||
|
message(STATUS "AddressSanitizer enabled")
|
||||||
|
set(SANITIZER_FLAGS "-fsanitize=address,undefined")
|
||||||
|
add_compile_options("-fno-sanitize=signed-integer-overflow")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(SPDLOG_SANITIZE_THREAD)
|
||||||
|
message(STATUS "ThreadSanitizer enabled")
|
||||||
|
set(SANITIZER_FLAGS "-fsanitize=thread")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(SPDLOG_SANITIZE_THREAD OR SPDLOG_SANITIZE_ADDRESS)
|
||||||
|
add_compile_options(${SANITIZER_FLAGS})
|
||||||
|
add_compile_options("-fno-sanitize-recover=all")
|
||||||
|
add_compile_options("-fno-omit-frame-pointer")
|
||||||
|
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${SANITIZER_FLAGS} -fuse-ld=gold")
|
||||||
|
endif()
|
6
third_party/spdlog/cmake/spdlog.pc.in
vendored
Normal file
6
third_party/spdlog/cmake/spdlog.pc.in
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
prefix=@CMAKE_INSTALL_PREFIX@
|
||||||
|
includedir=${prefix}/include
|
||||||
|
|
||||||
|
Name: @PROJECT_NAME@
|
||||||
|
Description: Super fast C++ logging library.
|
||||||
|
Version: @PROJECT_VERSION@
|
49
third_party/spdlog/example/CMakeLists.txt
vendored
Normal file
49
third_party/spdlog/example/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
# *************************************************************************/
|
||||||
|
# * Copyright (c) 2015 Ruslan Baratov. */
|
||||||
|
# * */
|
||||||
|
# * Permission is hereby granted, free of charge, to any person obtaining */
|
||||||
|
# * a copy of this software and associated documentation files (the */
|
||||||
|
# * "Software"), to deal in the Software without restriction, including */
|
||||||
|
# * without limitation the rights to use, copy, modify, merge, publish, */
|
||||||
|
# * distribute, sublicense, and/or sell copies of the Software, and to */
|
||||||
|
# * permit persons to whom the Software is furnished to do so, subject to */
|
||||||
|
# * the following conditions: */
|
||||||
|
# * */
|
||||||
|
# * The above copyright notice and this permission notice shall be */
|
||||||
|
# * included in all copies or substantial portions of the Software. */
|
||||||
|
# * */
|
||||||
|
# * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||||
|
# * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||||
|
# * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||||
|
# * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||||
|
# * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||||
|
# * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||||
|
# * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||||
|
# *************************************************************************/
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 3.1)
|
||||||
|
project(SpdlogExamples CXX)
|
||||||
|
|
||||||
|
if(NOT TARGET spdlog)
|
||||||
|
# Stand-alone build
|
||||||
|
find_package(spdlog CONFIG REQUIRED)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
find_package(Threads REQUIRED)
|
||||||
|
|
||||||
|
add_executable(example example.cpp)
|
||||||
|
if(CMAKE_SYSTEM_NAME STREQUAL "Android")
|
||||||
|
find_library(log-lib log)
|
||||||
|
target_link_libraries(example spdlog::spdlog Threads::Threads log)
|
||||||
|
else()
|
||||||
|
target_link_libraries(example spdlog::spdlog Threads::Threads)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
add_executable(multisink multisink.cpp)
|
||||||
|
target_link_libraries(multisink spdlog::spdlog Threads::Threads)
|
||||||
|
|
||||||
|
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/logs")
|
||||||
|
|
||||||
|
enable_testing()
|
||||||
|
add_test(NAME example COMMAND example)
|
22
third_party/spdlog/example/Makefile-all-warn
vendored
Normal file
22
third_party/spdlog/example/Makefile-all-warn
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#-Weverything -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-padded
|
||||||
|
CXX ?= g++
|
||||||
|
CXX_FLAGS = -Wall -Wextra -pedantic -std=c++11 -pthread -I../include -fmax-errors=1 -Wconversion -Weverything -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-padded -Wno-weak-vtables -Wno-global-constructors
|
||||||
|
CXX_RELEASE_FLAGS = -O3 -march=native
|
||||||
|
CXX_DEBUG_FLAGS= -g
|
||||||
|
|
||||||
|
all: example
|
||||||
|
debug: example-debug
|
||||||
|
|
||||||
|
example: example.cpp
|
||||||
|
$(CXX) example.cpp -o example $(CXX_FLAGS) $(CXX_RELEASE_FLAGS) $(CXXFLAGS)
|
||||||
|
|
||||||
|
|
||||||
|
example-debug: example.cpp
|
||||||
|
$(CXX) example.cpp -o example-debug $(CXX_FLAGS) $(CXX_DEBUG_FLAGS) $(CXXFLAGS)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.o logs/*.txt example example-debug
|
||||||
|
|
||||||
|
|
||||||
|
rebuild: clean all
|
||||||
|
rebuild-debug: clean debug
|
26
third_party/spdlog/example/Makefile.clang
vendored
Normal file
26
third_party/spdlog/example/Makefile.clang
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
CXX = clang++
|
||||||
|
CXXFLAGS = -march=native -Wall -Wextra -Wshadow -pedantic -std=c++11 -pthread -I../include
|
||||||
|
CXX_RELEASE_FLAGS = -O2
|
||||||
|
CXX_DEBUG_FLAGS= -g
|
||||||
|
|
||||||
|
|
||||||
|
all: example
|
||||||
|
debug: example-debug
|
||||||
|
|
||||||
|
example: example.cpp
|
||||||
|
$(CXX) example.cpp -o example-clang $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
example-debug: example.cpp
|
||||||
|
$(CXX) example.cpp -o example-clang-debug $(CXXFLAGS) $(CXX_DEBUG_FLAGS)
|
||||||
|
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.o logs/*.txt example-clang example-clang-debug
|
||||||
|
|
||||||
|
|
||||||
|
rebuild: clean all
|
||||||
|
rebuild-debug: clean debug
|
||||||
|
|
||||||
|
|
25
third_party/spdlog/example/Makefile.mingw
vendored
Normal file
25
third_party/spdlog/example/Makefile.mingw
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
CXX ?= g++
|
||||||
|
CXXFLAGS = -D_WIN32_WINNT=0x600 -march=native -Wall -Wextra -pedantic -std=gnu++0x -pthread -Wl,--no-as-needed -I../include
|
||||||
|
CXX_RELEASE_FLAGS = -O3
|
||||||
|
CXX_DEBUG_FLAGS= -g
|
||||||
|
|
||||||
|
|
||||||
|
all: example
|
||||||
|
debug: example-debug
|
||||||
|
|
||||||
|
example: example.cpp
|
||||||
|
$(CXX) example.cpp -o example $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
|
||||||
|
|
||||||
|
|
||||||
|
example-debug: example.cpp
|
||||||
|
$(CXX) example.cpp -o example-debug $(CXXFLAGS) $(CXX_DEBUG_FLAGS)
|
||||||
|
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.o logs/*.txt example example-debug
|
||||||
|
|
||||||
|
|
||||||
|
rebuild: clean all
|
||||||
|
rebuild-debug: clean debug
|
||||||
|
|
||||||
|
|
235
third_party/spdlog/example/example.cpp
vendored
Normal file
235
third_party/spdlog/example/example.cpp
vendored
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
//
|
||||||
|
// Copyright(c) 2015 Gabi Melman.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// spdlog usage example
|
||||||
|
//
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
void stdout_logger_example();
|
||||||
|
void basic_example();
|
||||||
|
void rotating_example();
|
||||||
|
void daily_example();
|
||||||
|
void async_example();
|
||||||
|
void binary_example();
|
||||||
|
void trace_example();
|
||||||
|
void multi_sink_example();
|
||||||
|
void user_defined_example();
|
||||||
|
void err_handler_example();
|
||||||
|
void syslog_example();
|
||||||
|
void clone_example();
|
||||||
|
|
||||||
|
#include "spdlog/spdlog.h"
|
||||||
|
|
||||||
|
int main(int, char *[])
|
||||||
|
{
|
||||||
|
spdlog::info("Welcome to spdlog version {}.{}.{} !", SPDLOG_VER_MAJOR, SPDLOG_VER_MINOR, SPDLOG_VER_PATCH);
|
||||||
|
spdlog::warn("Easy padding in numbers like {:08d}", 12);
|
||||||
|
spdlog::critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
|
||||||
|
spdlog::info("Support for floats {:03.2f}", 1.23456);
|
||||||
|
spdlog::info("Positional args are {1} {0}..", "too", "supported");
|
||||||
|
spdlog::info("{:>8} aligned, {:<8} aligned", "right", "left");
|
||||||
|
|
||||||
|
// Runtime log levels
|
||||||
|
spdlog::set_level(spdlog::level::info); // Set global log level to info
|
||||||
|
spdlog::debug("This message should not be displayed!");
|
||||||
|
spdlog::set_level(spdlog::level::trace); // Set specific logger's log level
|
||||||
|
spdlog::debug("This message should be displayed..");
|
||||||
|
|
||||||
|
// Customize msg format for all loggers
|
||||||
|
spdlog::set_pattern("[%H:%M:%S %z] [%^%L%$] [thread %t] %v");
|
||||||
|
spdlog::info("This an info message with custom format");
|
||||||
|
spdlog::set_pattern("%+"); // back to default format
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
stdout_logger_example();
|
||||||
|
basic_example();
|
||||||
|
rotating_example();
|
||||||
|
daily_example();
|
||||||
|
clone_example();
|
||||||
|
async_example();
|
||||||
|
binary_example();
|
||||||
|
multi_sink_example();
|
||||||
|
user_defined_example();
|
||||||
|
err_handler_example();
|
||||||
|
trace_example();
|
||||||
|
|
||||||
|
// Flush all *registered* loggers using a worker thread every 3 seconds.
|
||||||
|
// note: registered loggers *must* be thread safe for this to work correctly!
|
||||||
|
spdlog::flush_every(std::chrono::seconds(3));
|
||||||
|
|
||||||
|
// Apply some function on all registered loggers
|
||||||
|
spdlog::apply_all([&](std::shared_ptr<spdlog::logger> l) { l->info("End of example."); });
|
||||||
|
|
||||||
|
// Release all spdlog resources, and drop all loggers in the registry.
|
||||||
|
// This is optional (only mandatory if using windows + async log).
|
||||||
|
spdlog::shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exceptions will only be thrown upon failed logger or sink construction (not during logging).
|
||||||
|
catch (const spdlog::spdlog_ex &ex)
|
||||||
|
{
|
||||||
|
std::printf("Log initialization failed: %s\n", ex.what());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "spdlog/sinks/stdout_color_sinks.h"
|
||||||
|
// or #include "spdlog/sinks/stdout_sinks.h" if no colors needed.
|
||||||
|
void stdout_logger_example()
|
||||||
|
{
|
||||||
|
// Create color multi threaded logger.
|
||||||
|
auto console = spdlog::stdout_color_mt("console");
|
||||||
|
// or for stderr:
|
||||||
|
// auto console = spdlog::stderr_color_mt("error-logger");
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "spdlog/sinks/basic_file_sink.h"
|
||||||
|
void basic_example()
|
||||||
|
{
|
||||||
|
// Create basic file logger (not rotated).
|
||||||
|
auto my_logger = spdlog::basic_logger_mt("file_logger", "logs/basic-log.txt");
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "spdlog/sinks/rotating_file_sink.h"
|
||||||
|
void rotating_example()
|
||||||
|
{
|
||||||
|
// Create a file rotating logger with 5mb size max and 3 rotated files.
|
||||||
|
auto rotating_logger = spdlog::rotating_logger_mt("some_logger_name", "logs/rotating.txt", 1048576 * 5, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "spdlog/sinks/daily_file_sink.h"
|
||||||
|
void daily_example()
|
||||||
|
{
|
||||||
|
// Create a daily logger - a new file is created every day on 2:30am.
|
||||||
|
auto daily_logger = spdlog::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clone a logger and give it new name.
|
||||||
|
// Useful for creating component/subsystem loggers from some "root" logger.
|
||||||
|
void clone_example()
|
||||||
|
{
|
||||||
|
auto network_logger = spdlog::default_logger()->clone("network");
|
||||||
|
network_logger->info("Logging network stuff..");
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "spdlog/async.h"
|
||||||
|
void async_example()
|
||||||
|
{
|
||||||
|
// Default thread pool settings can be modified *before* creating the async logger:
|
||||||
|
// spdlog::init_thread_pool(32768, 1); // queue with max 32k items 1 backing thread.
|
||||||
|
auto async_file = spdlog::basic_logger_mt<spdlog::async_factory>("async_file_logger", "logs/async_log.txt");
|
||||||
|
// alternatively:
|
||||||
|
// auto async_file = spdlog::create_async<spdlog::sinks::basic_file_sink_mt>("async_file_logger", "logs/async_log.txt");
|
||||||
|
|
||||||
|
for (int i = 1; i < 101; ++i)
|
||||||
|
{
|
||||||
|
async_file->info("Async message #{}", i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log binary data as hex.
|
||||||
|
// Many types of std::container<char> types can be used.
|
||||||
|
// Iterator ranges are supported too.
|
||||||
|
// Format flags:
|
||||||
|
// {:X} - print in uppercase.
|
||||||
|
// {:s} - don't separate each byte with space.
|
||||||
|
// {:p} - don't print the position on each line start.
|
||||||
|
// {:n} - don't split the output to lines.
|
||||||
|
|
||||||
|
#include "spdlog/fmt/bin_to_hex.h"
|
||||||
|
void binary_example()
|
||||||
|
{
|
||||||
|
std::vector<char> buf;
|
||||||
|
for (int i = 0; i < 80; i++)
|
||||||
|
{
|
||||||
|
buf.push_back(static_cast<char>(i & 0xff));
|
||||||
|
}
|
||||||
|
spdlog::info("Binary example: {}", spdlog::to_hex(buf));
|
||||||
|
spdlog::info("Another binary example:{:n}", spdlog::to_hex(std::begin(buf), std::begin(buf) + 10));
|
||||||
|
// more examples:
|
||||||
|
// logger->info("uppercase: {:X}", spdlog::to_hex(buf));
|
||||||
|
// logger->info("uppercase, no delimiters: {:Xs}", spdlog::to_hex(buf));
|
||||||
|
// logger->info("uppercase, no delimiters, no position info: {:Xsp}", spdlog::to_hex(buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compile time log levels.
|
||||||
|
// define SPDLOG_ACTIVE_LEVEL to required level (e.g. SPDLOG_LEVEL_TRACE)
|
||||||
|
void trace_example()
|
||||||
|
{
|
||||||
|
// trace from default logger
|
||||||
|
SPDLOG_TRACE("Some trace message.. {} ,{}", 1, 3.23);
|
||||||
|
// debug from default logger
|
||||||
|
SPDLOG_DEBUG("Some debug message.. {} ,{}", 1, 3.23);
|
||||||
|
|
||||||
|
// trace from logger object
|
||||||
|
auto logger = spdlog::get("file_logger");
|
||||||
|
SPDLOG_LOGGER_TRACE(logger, "another trace message");
|
||||||
|
}
|
||||||
|
|
||||||
|
// A logger with multiple sinks (stdout and file) - each with a different format and log level.
|
||||||
|
void multi_sink_example()
|
||||||
|
{
|
||||||
|
auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
|
||||||
|
console_sink->set_level(spdlog::level::warn);
|
||||||
|
console_sink->set_pattern("[multi_sink_example] [%^%l%$] %v");
|
||||||
|
|
||||||
|
auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("logs/multisink.txt", true);
|
||||||
|
file_sink->set_level(spdlog::level::trace);
|
||||||
|
|
||||||
|
spdlog::logger logger("multi_sink", {console_sink, file_sink});
|
||||||
|
logger.set_level(spdlog::level::debug);
|
||||||
|
logger.warn("this should appear in both console and file");
|
||||||
|
logger.info("this message should not appear in the console, only in the file");
|
||||||
|
}
|
||||||
|
|
||||||
|
// User defined types logging by implementing operator<<
|
||||||
|
#include "spdlog/fmt/ostr.h" // must be included
|
||||||
|
struct my_type
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
template<typename OStream>
|
||||||
|
friend OStream &operator<<(OStream &os, const my_type &c)
|
||||||
|
{
|
||||||
|
return os << "[my_type i=" << c.i << "]";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void user_defined_example()
|
||||||
|
{
|
||||||
|
spdlog::info("user defined type: {}", my_type{14});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Custom error handler. Will be triggered on log failure.
|
||||||
|
void err_handler_example()
|
||||||
|
{
|
||||||
|
// can be set globally or per logger(logger->set_error_handler(..))
|
||||||
|
spdlog::set_error_handler([](const std::string &msg) { printf("*** Custom log error handler: %s ***\n", msg.c_str()); });
|
||||||
|
}
|
||||||
|
|
||||||
|
// syslog example (linux/osx/freebsd)
|
||||||
|
#ifndef _WIN32
|
||||||
|
#include "spdlog/sinks/syslog_sink.h"
|
||||||
|
void syslog_example()
|
||||||
|
{
|
||||||
|
std::string ident = "spdlog-example";
|
||||||
|
auto syslog_logger = spdlog::syslog_logger_mt("syslog", ident, LOG_PID);
|
||||||
|
syslog_logger->warn("This is warning that will end up in syslog.");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Android example.
|
||||||
|
#if defined(__ANDROID__)
|
||||||
|
#include "spdlog/sinks/android_sink.h"
|
||||||
|
void android_example()
|
||||||
|
{
|
||||||
|
std::string tag = "spdlog-android";
|
||||||
|
auto android_logger = spdlog::android_logger_mt("android", tag);
|
||||||
|
android_logger->critical("Use \"adb shell logcat\" to view this message.");
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
106
third_party/spdlog/example/example.sln
vendored
Normal file
106
third_party/spdlog/example/example.sln
vendored
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio 15
|
||||||
|
VisualStudioVersion = 15.0.27703.2018
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example", "example.vcxproj", "{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "spdlog", "spdlog", "{7FC6AB76-AD88-4135-888C-0568E81475AF}"
|
||||||
|
ProjectSection(SolutionItems) = preProject
|
||||||
|
..\include\spdlog\async.h = ..\include\spdlog\async.h
|
||||||
|
..\include\spdlog\async_logger.h = ..\include\spdlog\async_logger.h
|
||||||
|
..\include\spdlog\common.h = ..\include\spdlog\common.h
|
||||||
|
..\include\spdlog\formatter.h = ..\include\spdlog\formatter.h
|
||||||
|
..\include\spdlog\logger.h = ..\include\spdlog\logger.h
|
||||||
|
..\include\spdlog\spdlog.h = ..\include\spdlog\spdlog.h
|
||||||
|
..\include\spdlog\tweakme.h = ..\include\spdlog\tweakme.h
|
||||||
|
..\include\spdlog\version.h = ..\include\spdlog\version.h
|
||||||
|
EndProjectSection
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "details", "details", "{08E93803-E650-42D9-BBB4-3C16979F850E}"
|
||||||
|
ProjectSection(SolutionItems) = preProject
|
||||||
|
..\include\spdlog\details\async_logger_impl.h = ..\include\spdlog\details\async_logger_impl.h
|
||||||
|
..\include\spdlog\details\circular_q.h = ..\include\spdlog\details\circular_q.h
|
||||||
|
..\include\spdlog\details\console_globals.h = ..\include\spdlog\details\console_globals.h
|
||||||
|
..\include\spdlog\details\file_helper.h = ..\include\spdlog\details\file_helper.h
|
||||||
|
..\include\spdlog\details\fmt_helper.h = ..\include\spdlog\details\fmt_helper.h
|
||||||
|
..\include\spdlog\details\log_msg.h = ..\include\spdlog\details\log_msg.h
|
||||||
|
..\include\spdlog\details\logger_impl.h = ..\include\spdlog\details\logger_impl.h
|
||||||
|
..\include\spdlog\details\mpmc_blocking_q.h = ..\include\spdlog\details\mpmc_blocking_q.h
|
||||||
|
..\include\spdlog\details\null_mutex.h = ..\include\spdlog\details\null_mutex.h
|
||||||
|
..\include\spdlog\details\os.h = ..\include\spdlog\details\os.h
|
||||||
|
..\include\spdlog\details\pattern_formatter.h = ..\include\spdlog\details\pattern_formatter.h
|
||||||
|
..\include\spdlog\details\periodic_worker.h = ..\include\spdlog\details\periodic_worker.h
|
||||||
|
..\include\spdlog\details\registry.h = ..\include\spdlog\details\registry.h
|
||||||
|
..\include\spdlog\details\spdlog_impl.h = ..\include\spdlog\details\spdlog_impl.h
|
||||||
|
..\include\spdlog\details\thread_pool.h = ..\include\spdlog\details\thread_pool.h
|
||||||
|
EndProjectSection
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "fmt", "fmt", "{82378DE1-8463-4F91-91A0-C2C40E2AEA2A}"
|
||||||
|
ProjectSection(SolutionItems) = preProject
|
||||||
|
..\include\spdlog\fmt\fmt.h = ..\include\spdlog\fmt\fmt.h
|
||||||
|
..\include\spdlog\fmt\ostr.h = ..\include\spdlog\fmt\ostr.h
|
||||||
|
EndProjectSection
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "bundled", "bundled", "{D9CA4494-80D1-48D1-A897-D3564F7B27FF}"
|
||||||
|
ProjectSection(SolutionItems) = preProject
|
||||||
|
..\include\spdlog\fmt\bundled\format.cc = ..\include\spdlog\fmt\bundled\format.cc
|
||||||
|
..\include\spdlog\fmt\bundled\format.h = ..\include\spdlog\fmt\bundled\format.h
|
||||||
|
..\include\spdlog\fmt\bundled\LICENSE.rst = ..\include\spdlog\fmt\bundled\LICENSE.rst
|
||||||
|
..\include\spdlog\fmt\bundled\ostream.cc = ..\include\spdlog\fmt\bundled\ostream.cc
|
||||||
|
..\include\spdlog\fmt\bundled\ostream.h = ..\include\spdlog\fmt\bundled\ostream.h
|
||||||
|
..\include\spdlog\fmt\bundled\posix.cc = ..\include\spdlog\fmt\bundled\posix.cc
|
||||||
|
..\include\spdlog\fmt\bundled\posix.h = ..\include\spdlog\fmt\bundled\posix.h
|
||||||
|
..\include\spdlog\fmt\bundled\printf.cc = ..\include\spdlog\fmt\bundled\printf.cc
|
||||||
|
..\include\spdlog\fmt\bundled\printf.h = ..\include\spdlog\fmt\bundled\printf.h
|
||||||
|
..\include\spdlog\fmt\bundled\time.h = ..\include\spdlog\fmt\bundled\time.h
|
||||||
|
EndProjectSection
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sinks", "sinks", "{27D16BB9-2B81-4F61-80EC-0C7A777248E4}"
|
||||||
|
ProjectSection(SolutionItems) = preProject
|
||||||
|
..\include\spdlog\sinks\android_sink.h = ..\include\spdlog\sinks\android_sink.h
|
||||||
|
..\include\spdlog\sinks\ansicolor_sink.h = ..\include\spdlog\sinks\ansicolor_sink.h
|
||||||
|
..\include\spdlog\sinks\base_sink.h = ..\include\spdlog\sinks\base_sink.h
|
||||||
|
..\include\spdlog\sinks\dist_sink.h = ..\include\spdlog\sinks\dist_sink.h
|
||||||
|
..\include\spdlog\sinks\file_sinks.h = ..\include\spdlog\sinks\file_sinks.h
|
||||||
|
..\include\spdlog\sinks\msvc_sink.h = ..\include\spdlog\sinks\msvc_sink.h
|
||||||
|
..\include\spdlog\sinks\null_sink.h = ..\include\spdlog\sinks\null_sink.h
|
||||||
|
..\include\spdlog\sinks\ostream_sink.h = ..\include\spdlog\sinks\ostream_sink.h
|
||||||
|
..\include\spdlog\sinks\sink.h = ..\include\spdlog\sinks\sink.h
|
||||||
|
..\include\spdlog\sinks\stdout_color_sinks.h = ..\include\spdlog\sinks\stdout_color_sinks.h
|
||||||
|
..\include\spdlog\sinks\stdout_sinks.h = ..\include\spdlog\sinks\stdout_sinks.h
|
||||||
|
..\include\spdlog\sinks\syslog_sink.h = ..\include\spdlog\sinks\syslog_sink.h
|
||||||
|
..\include\spdlog\sinks\wincolor_sink.h = ..\include\spdlog\sinks\wincolor_sink.h
|
||||||
|
..\include\spdlog\sinks\windebug_sink.h = ..\include\spdlog\sinks\windebug_sink.h
|
||||||
|
EndProjectSection
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Win32 = Debug|Win32
|
||||||
|
Debug|x64 = Debug|x64
|
||||||
|
Release|Win32 = Release|Win32
|
||||||
|
Release|x64 = Release|x64
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||||
|
{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Debug|Win32.Build.0 = Debug|Win32
|
||||||
|
{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Release|Win32.ActiveCfg = Release|Win32
|
||||||
|
{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Release|Win32.Build.0 = Release|Win32
|
||||||
|
{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Release|x64.Build.0 = Release|x64
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(NestedProjects) = preSolution
|
||||||
|
{08E93803-E650-42D9-BBB4-3C16979F850E} = {7FC6AB76-AD88-4135-888C-0568E81475AF}
|
||||||
|
{82378DE1-8463-4F91-91A0-C2C40E2AEA2A} = {7FC6AB76-AD88-4135-888C-0568E81475AF}
|
||||||
|
{D9CA4494-80D1-48D1-A897-D3564F7B27FF} = {82378DE1-8463-4F91-91A0-C2C40E2AEA2A}
|
||||||
|
{27D16BB9-2B81-4F61-80EC-0C7A777248E4} = {7FC6AB76-AD88-4135-888C-0568E81475AF}
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {1BF53532-C5DC-4236-B195-9E17CBE40A48}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
167
third_party/spdlog/example/example.vcxproj
vendored
Normal file
167
third_party/spdlog/example/example.vcxproj
vendored
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<ItemGroup Label="ProjectConfigurations">
|
||||||
|
<ProjectConfiguration Include="Debug|Win32">
|
||||||
|
<Configuration>Debug</Configuration>
|
||||||
|
<Platform>Win32</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Debug|x64">
|
||||||
|
<Configuration>Debug</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release|Win32">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>Win32</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release|x64">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="example.cpp" />
|
||||||
|
</ItemGroup>
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<ProjectGuid>{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}</ProjectGuid>
|
||||||
|
<Keyword>Win32Proj</Keyword>
|
||||||
|
<RootNamespace>.</RootNamespace>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>true</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v141</PlatformToolset>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>true</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v141</PlatformToolset>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v141</PlatformToolset>
|
||||||
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v141</PlatformToolset>
|
||||||
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||||
|
<ImportGroup Label="ExtensionSettings">
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<PropertyGroup Label="UserMacros" />
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
|
<LinkIncremental>true</LinkIncremental>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
<LinkIncremental>true</LinkIncremental>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
|
<LinkIncremental>false</LinkIncremental>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<LinkIncremental>false</LinkIncremental>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
|
<ClCompile>
|
||||||
|
<PrecompiledHeader>
|
||||||
|
</PrecompiledHeader>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<Optimization>Disabled</Optimization>
|
||||||
|
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<AdditionalIncludeDirectories>..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
<PrecompiledHeaderFile />
|
||||||
|
<PrecompiledHeaderOutputFile />
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Console</SubSystem>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
<ClCompile>
|
||||||
|
<PrecompiledHeader>
|
||||||
|
</PrecompiledHeader>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<Optimization>Disabled</Optimization>
|
||||||
|
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<AdditionalIncludeDirectories>..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
<PrecompiledHeaderFile>
|
||||||
|
</PrecompiledHeaderFile>
|
||||||
|
<PrecompiledHeaderOutputFile>
|
||||||
|
</PrecompiledHeaderOutputFile>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Console</SubSystem>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
|
<ClCompile>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<PrecompiledHeader>
|
||||||
|
</PrecompiledHeader>
|
||||||
|
<Optimization>MaxSpeed</Optimization>
|
||||||
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
|
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<AdditionalIncludeDirectories>..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
<PrecompiledHeaderFile />
|
||||||
|
<PrecompiledHeaderOutputFile />
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Console</SubSystem>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
|
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||||
|
<AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<ClCompile>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<PrecompiledHeader>
|
||||||
|
</PrecompiledHeader>
|
||||||
|
<Optimization>MaxSpeed</Optimization>
|
||||||
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
|
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<AdditionalIncludeDirectories>..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
<PrecompiledHeaderFile>
|
||||||
|
</PrecompiledHeaderFile>
|
||||||
|
<PrecompiledHeaderOutputFile>
|
||||||
|
</PrecompiledHeaderOutputFile>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Console</SubSystem>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
|
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||||
|
<AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
</ImportGroup>
|
||||||
|
</Project>
|
15
third_party/spdlog/example/jni/Android.mk
vendored
Normal file
15
third_party/spdlog/example/jni/Android.mk
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# Setup a project
|
||||||
|
LOCAL_PATH := $(call my-dir)
|
||||||
|
include $(CLEAR_VARS)
|
||||||
|
|
||||||
|
LOCAL_MODULE := example
|
||||||
|
LOCAL_SRC_FILES := example.cpp
|
||||||
|
LOCAL_CPPFLAGS += -Wall -Wshadow -Wextra -pedantic -std=c++11 -fPIE -pie
|
||||||
|
LOCAL_LDFLAGS += -fPIE -pie
|
||||||
|
|
||||||
|
# Add exception support and set path for spdlog's headers
|
||||||
|
LOCAL_CPPFLAGS += -fexceptions -I../include
|
||||||
|
# Use android's log library
|
||||||
|
LOCAL_LDFLAGS += -llog
|
||||||
|
|
||||||
|
include $(BUILD_EXECUTABLE)
|
2
third_party/spdlog/example/jni/Application.mk
vendored
Normal file
2
third_party/spdlog/example/jni/Application.mk
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
# Exceptions are used in spdlog. Link to an exception-ready C++ runtime.
|
||||||
|
APP_STL = gnustl_static
|
157
third_party/spdlog/example/jni/example.cpp
vendored
Normal file
157
third_party/spdlog/example/jni/example.cpp
vendored
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
//
|
||||||
|
// Copyright(c) 2015 Gabi Melman.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// spdlog usage example
|
||||||
|
//
|
||||||
|
//
|
||||||
|
|
||||||
|
#define SPDLOG_TRACE_ON
|
||||||
|
#define SPDLOG_DEBUG_ON
|
||||||
|
|
||||||
|
#include "spdlog/spdlog.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
void async_example();
|
||||||
|
void syslog_example();
|
||||||
|
void android_example();
|
||||||
|
void user_defined_example();
|
||||||
|
void err_handler_example();
|
||||||
|
|
||||||
|
namespace spd = spdlog;
|
||||||
|
int main(int, char *[])
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Console logger with color
|
||||||
|
auto console = spd::stdout_color_mt("console");
|
||||||
|
console->info("Welcome to spdlog!");
|
||||||
|
console->error("Some error message with arg{}..", 1);
|
||||||
|
|
||||||
|
// Formatting examples
|
||||||
|
console->warn("Easy padding in numbers like {:08d}", 12);
|
||||||
|
console->critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
|
||||||
|
console->info("Support for floats {:03.2f}", 1.23456);
|
||||||
|
console->info("Positional args are {1} {0}..", "too", "supported");
|
||||||
|
console->info("{:<30}", "left aligned");
|
||||||
|
|
||||||
|
spd::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name) function");
|
||||||
|
|
||||||
|
// Create basic file logger (not rotated)
|
||||||
|
auto my_logger = spd::basic_logger_mt("basic_logger", "logs/basic-log.txt");
|
||||||
|
my_logger->info("Some log message");
|
||||||
|
|
||||||
|
// Create a file rotating logger with 5mb size max and 3 rotated files
|
||||||
|
auto rotating_logger = spd::rotating_logger_mt("some_logger_name", "logs/rotating.txt", 1048576 * 5, 3);
|
||||||
|
for (int i = 0; i < 10; ++i)
|
||||||
|
rotating_logger->info("{} * {} equals {:>10}", i, i, i * i);
|
||||||
|
|
||||||
|
// Create a daily logger - a new file is created every day on 2:30am
|
||||||
|
auto daily_logger = spd::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30);
|
||||||
|
// trigger flush if the log severity is error or higher
|
||||||
|
daily_logger->flush_on(spd::level::err);
|
||||||
|
daily_logger->info(123.44);
|
||||||
|
|
||||||
|
// Customize msg format for all messages
|
||||||
|
spd::set_pattern("*** [%H:%M:%S %z] [thread %t] %v ***");
|
||||||
|
rotating_logger->info("This is another message with custom format");
|
||||||
|
|
||||||
|
// Runtime log levels
|
||||||
|
spd::set_level(spd::level::info); // Set global log level to info
|
||||||
|
console->debug("This message should not be displayed!");
|
||||||
|
console->set_level(spd::level::debug); // Set specific logger's log level
|
||||||
|
console->debug("This message should be displayed..");
|
||||||
|
|
||||||
|
// Compile time log levels
|
||||||
|
// define SPDLOG_DEBUG_ON or SPDLOG_TRACE_ON
|
||||||
|
SPDLOG_TRACE(console, "Enabled only #ifdef SPDLOG_TRACE_ON..{} ,{}", 1, 3.23);
|
||||||
|
SPDLOG_DEBUG(console, "Enabled only #ifdef SPDLOG_DEBUG_ON.. {} ,{}", 1, 3.23);
|
||||||
|
|
||||||
|
// Asynchronous logging is very fast..
|
||||||
|
// Just call spdlog::set_async_mode(q_size) and all created loggers from now on will be asynchronous..
|
||||||
|
async_example();
|
||||||
|
|
||||||
|
// syslog example. linux/osx only
|
||||||
|
syslog_example();
|
||||||
|
|
||||||
|
// android example. compile with NDK
|
||||||
|
android_example();
|
||||||
|
|
||||||
|
// Log user-defined types example
|
||||||
|
user_defined_example();
|
||||||
|
|
||||||
|
// Change default log error handler
|
||||||
|
err_handler_example();
|
||||||
|
|
||||||
|
// Apply a function on all registered loggers
|
||||||
|
spd::apply_all([&](std::shared_ptr<spdlog::logger> l) { l->info("End of example."); });
|
||||||
|
|
||||||
|
// Release and close all loggers
|
||||||
|
spdlog::drop_all();
|
||||||
|
}
|
||||||
|
// Exceptions will only be thrown upon failed logger or sink construction (not during logging)
|
||||||
|
catch (const spd::spdlog_ex &ex)
|
||||||
|
{
|
||||||
|
std::cout << "Log init failed: " << ex.what() << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void async_example()
|
||||||
|
{
|
||||||
|
size_t q_size = 4096; // queue size must be power of 2
|
||||||
|
spdlog::set_async_mode(q_size);
|
||||||
|
auto async_file = spd::daily_logger_st("async_file_logger", "logs/async_log.txt");
|
||||||
|
for (int i = 0; i < 100; ++i)
|
||||||
|
async_file->info("Async message #{}", i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// syslog example (linux/osx/freebsd)
|
||||||
|
void syslog_example()
|
||||||
|
{
|
||||||
|
#ifdef SPDLOG_ENABLE_SYSLOG
|
||||||
|
std::string ident = "spdlog-example";
|
||||||
|
auto syslog_logger = spd::syslog_logger("syslog", ident, LOG_PID);
|
||||||
|
syslog_logger->warn("This is warning that will end up in syslog.");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Android example
|
||||||
|
void android_example()
|
||||||
|
{
|
||||||
|
#if defined(__ANDROID__)
|
||||||
|
std::string tag = "spdlog-android";
|
||||||
|
auto android_logger = spd::android_logger("android", tag);
|
||||||
|
android_logger->critical("Use \"adb shell logcat\" to view this message.");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// user defined types logging by implementing operator<<
|
||||||
|
struct my_type
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
template<typename OStream>
|
||||||
|
friend OStream &operator<<(OStream &os, const my_type &c)
|
||||||
|
{
|
||||||
|
return os << "[my_type i=" << c.i << "]";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#include "spdlog/fmt/ostr.h" // must be included
|
||||||
|
void user_defined_example()
|
||||||
|
{
|
||||||
|
spd::get("console")->info("user defined type: {}", my_type{14});
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// custom error handler
|
||||||
|
//
|
||||||
|
void err_handler_example()
|
||||||
|
{
|
||||||
|
// can be set globaly or per logger(logger->set_error_handler(..))
|
||||||
|
spdlog::set_error_handler([](const std::string &msg) { std::cerr << "my err handler: " << msg << std::endl; });
|
||||||
|
spd::get("console")->info("some invalid message to trigger an error {}{}{}{}", 3);
|
||||||
|
}
|
47
third_party/spdlog/example/multisink.cpp
vendored
Normal file
47
third_party/spdlog/example/multisink.cpp
vendored
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#include "spdlog/spdlog.h"
|
||||||
|
#include "spdlog/sinks/basic_file_sink.h"
|
||||||
|
#include "spdlog/sinks/stdout_sinks.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
int main(int, char *[])
|
||||||
|
{
|
||||||
|
bool enable_debug = true;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// This other example use a single logger with multiple sinks.
|
||||||
|
// This means that the same log_msg is forwarded to multiple sinks;
|
||||||
|
// Each sink can have it's own log level and a message will be logged.
|
||||||
|
std::vector<spdlog::sink_ptr> sinks;
|
||||||
|
sinks.push_back(std::make_shared<spdlog::sinks::stdout_sink_mt>());
|
||||||
|
sinks.push_back(std::make_shared<spdlog::sinks::basic_file_sink_mt>("./log_regular_file.txt"));
|
||||||
|
sinks.push_back(std::make_shared<spdlog::sinks::basic_file_sink_mt>("./log_debug_file.txt"));
|
||||||
|
|
||||||
|
spdlog::logger console_multisink("multisink", sinks.begin(), sinks.end());
|
||||||
|
console_multisink.set_level(spdlog::level::warn);
|
||||||
|
|
||||||
|
sinks[0]->set_level(spdlog::level::trace); // console. Allow everything. Default value
|
||||||
|
sinks[1]->set_level(spdlog::level::trace); // regular file. Allow everything. Default value
|
||||||
|
sinks[2]->set_level(spdlog::level::off); // regular file. Ignore everything.
|
||||||
|
|
||||||
|
console_multisink.warn("warn: will print only on console and regular file");
|
||||||
|
|
||||||
|
if (enable_debug)
|
||||||
|
{
|
||||||
|
console_multisink.set_level(spdlog::level::debug); // level of the logger
|
||||||
|
sinks[1]->set_level(spdlog::level::debug); // regular file
|
||||||
|
sinks[2]->set_level(spdlog::level::debug); // debug file
|
||||||
|
}
|
||||||
|
console_multisink.debug("Debug: you should see this on console and both files");
|
||||||
|
|
||||||
|
// Release and close all loggers
|
||||||
|
spdlog::drop_all();
|
||||||
|
}
|
||||||
|
// Exceptions will only be thrown upon failed logger or sink construction (not during logging)
|
||||||
|
catch (const spdlog::spdlog_ex &ex)
|
||||||
|
{
|
||||||
|
std::cout << "Log init failed: " << ex.what() << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
34
third_party/spdlog/example/utils.h
vendored
Normal file
34
third_party/spdlog/example/utils.h
vendored
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
//
|
||||||
|
// Copyright(c) 2015 Gabi Melman.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <iomanip>
|
||||||
|
#include <locale>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
namespace utils {
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline std::string format(const T &value)
|
||||||
|
{
|
||||||
|
static std::locale loc("");
|
||||||
|
std::stringstream ss;
|
||||||
|
ss.imbue(loc);
|
||||||
|
ss << value;
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
inline std::string format(const double &value)
|
||||||
|
{
|
||||||
|
static std::locale loc("");
|
||||||
|
std::stringstream ss;
|
||||||
|
ss.imbue(loc);
|
||||||
|
ss << std::fixed << std::setprecision(1) << value;
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace utils
|
9
third_party/spdlog/format.sh
vendored
Executable file
9
third_party/spdlog/format.sh
vendored
Executable file
@ -0,0 +1,9 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
echo -n "Running dos2unix "
|
||||||
|
find . -name "*\.h" -o -name "*\.cpp"|grep -v bundled|xargs -I {} sh -c "dos2unix '{}' 2>/dev/null; echo -n '.'"
|
||||||
|
echo
|
||||||
|
echo -n "Running clang-format "
|
||||||
|
find . -name "*\.h" -o -name "*\.cpp"|grep -v bundled|xargs -I {} sh -c "clang-format -i {}; echo -n '.'"
|
||||||
|
echo
|
||||||
|
|
||||||
|
|
87
third_party/spdlog/include/spdlog/async.h
vendored
Normal file
87
third_party/spdlog/include/spdlog/async.h
vendored
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
|
||||||
|
//
|
||||||
|
// Copyright(c) 2018 Gabi Melman.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
//
|
||||||
|
// Async logging using global thread pool
|
||||||
|
// All loggers created here share same global thread pool.
|
||||||
|
// Each log message is pushed to a queue along withe a shared pointer to the
|
||||||
|
// logger.
|
||||||
|
// If a logger deleted while having pending messages in the queue, it's actual
|
||||||
|
// destruction will defer
|
||||||
|
// until all its messages are processed by the thread pool.
|
||||||
|
// This is because each message in the queue holds a shared_ptr to the
|
||||||
|
// originating logger.
|
||||||
|
|
||||||
|
#include "spdlog/async_logger.h"
|
||||||
|
#include "spdlog/details/registry.h"
|
||||||
|
#include "spdlog/details/thread_pool.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
|
||||||
|
namespace details {
|
||||||
|
static const size_t default_async_q_size = 8192;
|
||||||
|
}
|
||||||
|
|
||||||
|
// async logger factory - creates async loggers backed with thread pool.
|
||||||
|
// if a global thread pool doesn't already exist, create it with default queue
|
||||||
|
// size of 8192 items and single thread.
|
||||||
|
template<async_overflow_policy OverflowPolicy = async_overflow_policy::block>
|
||||||
|
struct async_factory_impl
|
||||||
|
{
|
||||||
|
template<typename Sink, typename... SinkArgs>
|
||||||
|
static std::shared_ptr<async_logger> create(std::string logger_name, SinkArgs &&... args)
|
||||||
|
{
|
||||||
|
auto ®istry_inst = details::registry::instance();
|
||||||
|
|
||||||
|
// create global thread pool if not already exists..
|
||||||
|
std::lock_guard<std::recursive_mutex> tp_lock(registry_inst.tp_mutex());
|
||||||
|
auto tp = registry_inst.get_tp();
|
||||||
|
if (tp == nullptr)
|
||||||
|
{
|
||||||
|
tp = std::make_shared<details::thread_pool>(details::default_async_q_size, 1);
|
||||||
|
registry_inst.set_tp(tp);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto sink = std::make_shared<Sink>(std::forward<SinkArgs>(args)...);
|
||||||
|
auto new_logger = std::make_shared<async_logger>(std::move(logger_name), std::move(sink), std::move(tp), OverflowPolicy);
|
||||||
|
registry_inst.initialize_logger(new_logger);
|
||||||
|
return new_logger;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using async_factory = async_factory_impl<async_overflow_policy::block>;
|
||||||
|
using async_factory_nonblock = async_factory_impl<async_overflow_policy::overrun_oldest>;
|
||||||
|
|
||||||
|
template<typename Sink, typename... SinkArgs>
|
||||||
|
inline std::shared_ptr<spdlog::logger> create_async(std::string logger_name, SinkArgs &&... sink_args)
|
||||||
|
{
|
||||||
|
return async_factory::create<Sink>(std::move(logger_name), std::forward<SinkArgs>(sink_args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Sink, typename... SinkArgs>
|
||||||
|
inline std::shared_ptr<spdlog::logger> create_async_nb(std::string logger_name, SinkArgs &&... sink_args)
|
||||||
|
{
|
||||||
|
return async_factory_nonblock::create<Sink>(std::move(logger_name), std::forward<SinkArgs>(sink_args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
// set global thread pool.
|
||||||
|
inline void init_thread_pool(size_t q_size, size_t thread_count)
|
||||||
|
{
|
||||||
|
auto tp = std::make_shared<details::thread_pool>(q_size, thread_count);
|
||||||
|
details::registry::instance().set_tp(std::move(tp));
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the global thread pool.
|
||||||
|
inline std::shared_ptr<spdlog::details::thread_pool> thread_pool()
|
||||||
|
{
|
||||||
|
return details::registry::instance().get_tp();
|
||||||
|
}
|
||||||
|
} // namespace spdlog
|
73
third_party/spdlog/include/spdlog/async_logger.h
vendored
Normal file
73
third_party/spdlog/include/spdlog/async_logger.h
vendored
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
//
|
||||||
|
// Copyright(c) 2015 Gabi Melman.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Very fast asynchronous logger (millions of logs per second on an average
|
||||||
|
// desktop)
|
||||||
|
// Uses pre allocated lockfree queue for maximum throughput even under large
|
||||||
|
// number of threads.
|
||||||
|
// Creates a single back thread to pop messages from the queue and log them.
|
||||||
|
//
|
||||||
|
// Upon each log write the logger:
|
||||||
|
// 1. Checks if its log level is enough to log the message
|
||||||
|
// 2. Push a new copy of the message to a queue (or block the caller until
|
||||||
|
// space is available in the queue)
|
||||||
|
// 3. will throw spdlog_ex upon log exceptions
|
||||||
|
// Upon destruction, logs all remaining messages in the queue before
|
||||||
|
// destructing..
|
||||||
|
|
||||||
|
#include "spdlog/common.h"
|
||||||
|
#include "spdlog/logger.h"
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
|
||||||
|
// Async overflow policy - block by default.
|
||||||
|
enum class async_overflow_policy
|
||||||
|
{
|
||||||
|
block, // Block until message can be enqueued
|
||||||
|
overrun_oldest // Discard oldest message in the queue if full when trying to
|
||||||
|
// add new item.
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace details {
|
||||||
|
class thread_pool;
|
||||||
|
}
|
||||||
|
|
||||||
|
class async_logger final : public std::enable_shared_from_this<async_logger>, public logger
|
||||||
|
{
|
||||||
|
friend class details::thread_pool;
|
||||||
|
|
||||||
|
public:
|
||||||
|
template<typename It>
|
||||||
|
async_logger(std::string logger_name, It begin, It end, std::weak_ptr<details::thread_pool> tp,
|
||||||
|
async_overflow_policy overflow_policy = async_overflow_policy::block);
|
||||||
|
|
||||||
|
async_logger(std::string logger_name, sinks_init_list sinks_list, std::weak_ptr<details::thread_pool> tp,
|
||||||
|
async_overflow_policy overflow_policy = async_overflow_policy::block);
|
||||||
|
|
||||||
|
async_logger(std::string logger_name, sink_ptr single_sink, std::weak_ptr<details::thread_pool> tp,
|
||||||
|
async_overflow_policy overflow_policy = async_overflow_policy::block);
|
||||||
|
|
||||||
|
std::shared_ptr<logger> clone(std::string new_name) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void sink_it_(details::log_msg &msg) override;
|
||||||
|
void flush_() override;
|
||||||
|
|
||||||
|
void backend_log_(const details::log_msg &incoming_log_msg);
|
||||||
|
void backend_flush_();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::weak_ptr<details::thread_pool> thread_pool_;
|
||||||
|
async_overflow_policy overflow_policy_;
|
||||||
|
};
|
||||||
|
} // namespace spdlog
|
||||||
|
|
||||||
|
#include "details/async_logger_impl.h"
|
243
third_party/spdlog/include/spdlog/common.h
vendored
Normal file
243
third_party/spdlog/include/spdlog/common.h
vendored
Normal file
@ -0,0 +1,243 @@
|
|||||||
|
//
|
||||||
|
// Copyright(c) 2015 Gabi Melman.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "spdlog/tweakme.h"
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <chrono>
|
||||||
|
#include <functional>
|
||||||
|
#include <initializer_list>
|
||||||
|
#include <memory>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
#include <cstring>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT)
|
||||||
|
#include <codecvt>
|
||||||
|
#include <locale>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "spdlog/details/null_mutex.h"
|
||||||
|
|
||||||
|
#include "spdlog/fmt/fmt.h"
|
||||||
|
|
||||||
|
// visual studio upto 2013 does not support noexcept nor constexpr
|
||||||
|
#if defined(_MSC_VER) && (_MSC_VER < 1900)
|
||||||
|
#define SPDLOG_NOEXCEPT throw()
|
||||||
|
#define SPDLOG_CONSTEXPR
|
||||||
|
#else
|
||||||
|
#define SPDLOG_NOEXCEPT noexcept
|
||||||
|
#define SPDLOG_CONSTEXPR constexpr
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__GNUC__) || defined(__clang__)
|
||||||
|
#define SPDLOG_DEPRECATED __attribute__((deprecated))
|
||||||
|
#elif defined(_MSC_VER)
|
||||||
|
#define SPDLOG_DEPRECATED __declspec(deprecated)
|
||||||
|
#else
|
||||||
|
#define SPDLOG_DEPRECATED
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// disable thread local on msvc 2013
|
||||||
|
#ifndef SPDLOG_NO_TLS
|
||||||
|
#if (defined(_MSC_VER) && (_MSC_VER < 1900)) || defined(__cplusplus_winrt)
|
||||||
|
#define SPDLOG_NO_TLS 1
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Get the basename of __FILE__ (at compile time if possible)
|
||||||
|
#if FMT_HAS_FEATURE(__builtin_strrchr)
|
||||||
|
#define SPDLOG_STRRCHR(str, sep) __builtin_strrchr(str, sep)
|
||||||
|
#else
|
||||||
|
#define SPDLOG_STRRCHR(str, sep) strrchr(str, sep)
|
||||||
|
#endif //__builtin_strrchr not defined
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#define SPDLOG_FILE_BASENAME(file) SPDLOG_STRRCHR("\\" file, '\\') + 1
|
||||||
|
#else
|
||||||
|
#define SPDLOG_FILE_BASENAME(file) SPDLOG_STRRCHR("/" file, '/') + 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SPDLOG_FUNCTION
|
||||||
|
#define SPDLOG_FUNCTION __FUNCTION__
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
|
||||||
|
class formatter;
|
||||||
|
|
||||||
|
namespace sinks {
|
||||||
|
class sink;
|
||||||
|
}
|
||||||
|
|
||||||
|
using log_clock = std::chrono::system_clock;
|
||||||
|
using sink_ptr = std::shared_ptr<sinks::sink>;
|
||||||
|
using sinks_init_list = std::initializer_list<sink_ptr>;
|
||||||
|
using log_err_handler = std::function<void(const std::string &err_msg)>;
|
||||||
|
|
||||||
|
// string_view type - either std::string_view or fmt::string_view (pre c++17)
|
||||||
|
#if defined(FMT_USE_STD_STRING_VIEW)
|
||||||
|
using string_view_t = std::string_view;
|
||||||
|
#else
|
||||||
|
using string_view_t = fmt::string_view;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(SPDLOG_NO_ATOMIC_LEVELS)
|
||||||
|
using level_t = details::null_atomic_int;
|
||||||
|
#else
|
||||||
|
using level_t = std::atomic<int>;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define SPDLOG_LEVEL_TRACE 0
|
||||||
|
#define SPDLOG_LEVEL_DEBUG 1
|
||||||
|
#define SPDLOG_LEVEL_INFO 2
|
||||||
|
#define SPDLOG_LEVEL_WARN 3
|
||||||
|
#define SPDLOG_LEVEL_ERROR 4
|
||||||
|
#define SPDLOG_LEVEL_CRITICAL 5
|
||||||
|
#define SPDLOG_LEVEL_OFF 6
|
||||||
|
|
||||||
|
#if !defined(SPDLOG_ACTIVE_LEVEL)
|
||||||
|
#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Log level enum
|
||||||
|
namespace level {
|
||||||
|
enum level_enum
|
||||||
|
{
|
||||||
|
trace = SPDLOG_LEVEL_TRACE,
|
||||||
|
debug = SPDLOG_LEVEL_DEBUG,
|
||||||
|
info = SPDLOG_LEVEL_INFO,
|
||||||
|
warn = SPDLOG_LEVEL_WARN,
|
||||||
|
err = SPDLOG_LEVEL_ERROR,
|
||||||
|
critical = SPDLOG_LEVEL_CRITICAL,
|
||||||
|
off = SPDLOG_LEVEL_OFF,
|
||||||
|
};
|
||||||
|
|
||||||
|
#if !defined(SPDLOG_LEVEL_NAMES)
|
||||||
|
#define SPDLOG_LEVEL_NAMES \
|
||||||
|
{ \
|
||||||
|
"trace", "debug", "info", "warning", "error", "critical", "off" \
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static string_view_t level_string_views[] SPDLOG_LEVEL_NAMES;
|
||||||
|
static const char *short_level_names[]{"T", "D", "I", "W", "E", "C", "O"};
|
||||||
|
|
||||||
|
inline string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
return level_string_views[l];
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
return short_level_names[l];
|
||||||
|
}
|
||||||
|
|
||||||
|
inline spdlog::level::level_enum from_str(const std::string &name) SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
int level = 0;
|
||||||
|
for (const auto &level_str : level_string_views)
|
||||||
|
{
|
||||||
|
if (level_str == name)
|
||||||
|
{
|
||||||
|
return static_cast<level::level_enum>(level);
|
||||||
|
}
|
||||||
|
level++;
|
||||||
|
}
|
||||||
|
return level::off;
|
||||||
|
}
|
||||||
|
|
||||||
|
using level_hasher = std::hash<int>;
|
||||||
|
} // namespace level
|
||||||
|
|
||||||
|
//
|
||||||
|
// Pattern time - specific time getting to use for pattern_formatter.
|
||||||
|
// local time by default
|
||||||
|
//
|
||||||
|
enum class pattern_time_type
|
||||||
|
{
|
||||||
|
local, // log localtime
|
||||||
|
utc // log utc
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// Log exception
|
||||||
|
//
|
||||||
|
class spdlog_ex : public std::exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit spdlog_ex(std::string msg)
|
||||||
|
: msg_(std::move(msg))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
spdlog_ex(const std::string &msg, int last_errno)
|
||||||
|
{
|
||||||
|
fmt::memory_buffer outbuf;
|
||||||
|
fmt::format_system_error(outbuf, last_errno, msg);
|
||||||
|
msg_ = fmt::to_string(outbuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *what() const SPDLOG_NOEXCEPT override
|
||||||
|
{
|
||||||
|
return msg_.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string msg_;
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined)
|
||||||
|
//
|
||||||
|
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
|
||||||
|
using filename_t = std::wstring;
|
||||||
|
#else
|
||||||
|
using filename_t = std::string;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct source_loc
|
||||||
|
{
|
||||||
|
SPDLOG_CONSTEXPR source_loc()
|
||||||
|
: filename{""}
|
||||||
|
, line{0}
|
||||||
|
, funcname{""}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
SPDLOG_CONSTEXPR source_loc(const char *filename, int line, const char *funcname)
|
||||||
|
: filename{filename}
|
||||||
|
, line{static_cast<uint32_t>(line)}
|
||||||
|
, funcname{funcname}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_CONSTEXPR bool empty() const SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
return line == 0;
|
||||||
|
}
|
||||||
|
const char *filename;
|
||||||
|
uint32_t line;
|
||||||
|
const char *funcname;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace details {
|
||||||
|
// make_unique support for pre c++14
|
||||||
|
|
||||||
|
#if __cplusplus >= 201402L // C++14 and beyond
|
||||||
|
using std::make_unique;
|
||||||
|
#else
|
||||||
|
template<typename T, typename... Args>
|
||||||
|
std::unique_ptr<T> make_unique(Args &&... args)
|
||||||
|
{
|
||||||
|
static_assert(!std::is_array<T>::value, "arrays not supported");
|
||||||
|
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
} // namespace details
|
||||||
|
} // namespace spdlog
|
110
third_party/spdlog/include/spdlog/details/async_logger_impl.h
vendored
Normal file
110
third_party/spdlog/include/spdlog/details/async_logger_impl.h
vendored
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
//
|
||||||
|
// Copyright(c) 2015 Gabi Melman.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// async logger implementation
|
||||||
|
// uses a thread pool to perform the actual logging
|
||||||
|
|
||||||
|
#include "spdlog/details/thread_pool.h"
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
template<typename It>
|
||||||
|
inline spdlog::async_logger::async_logger(
|
||||||
|
std::string logger_name, It begin, It end, std::weak_ptr<details::thread_pool> tp, async_overflow_policy overflow_policy)
|
||||||
|
: logger(std::move(logger_name), begin, end)
|
||||||
|
, thread_pool_(std::move(tp))
|
||||||
|
, overflow_policy_(overflow_policy)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
inline spdlog::async_logger::async_logger(
|
||||||
|
std::string logger_name, sinks_init_list sinks_list, std::weak_ptr<details::thread_pool> tp, async_overflow_policy overflow_policy)
|
||||||
|
: async_logger(std::move(logger_name), sinks_list.begin(), sinks_list.end(), std::move(tp), overflow_policy)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
inline spdlog::async_logger::async_logger(
|
||||||
|
std::string logger_name, sink_ptr single_sink, std::weak_ptr<details::thread_pool> tp, async_overflow_policy overflow_policy)
|
||||||
|
: async_logger(std::move(logger_name), {std::move(single_sink)}, std::move(tp), overflow_policy)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// send the log message to the thread pool
|
||||||
|
inline void spdlog::async_logger::sink_it_(details::log_msg &msg)
|
||||||
|
{
|
||||||
|
#if defined(SPDLOG_ENABLE_MESSAGE_COUNTER)
|
||||||
|
incr_msg_counter_(msg);
|
||||||
|
#endif
|
||||||
|
if (auto pool_ptr = thread_pool_.lock())
|
||||||
|
{
|
||||||
|
pool_ptr->post_log(shared_from_this(), msg, overflow_policy_);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw spdlog_ex("async log: thread pool doesn't exist anymore");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// send flush request to the thread pool
|
||||||
|
inline void spdlog::async_logger::flush_()
|
||||||
|
{
|
||||||
|
if (auto pool_ptr = thread_pool_.lock())
|
||||||
|
{
|
||||||
|
pool_ptr->post_flush(shared_from_this(), overflow_policy_);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw spdlog_ex("async flush: thread pool doesn't exist anymore");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// backend functions - called from the thread pool to do the actual job
|
||||||
|
//
|
||||||
|
inline void spdlog::async_logger::backend_log_(const details::log_msg &incoming_log_msg)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
for (auto &s : sinks_)
|
||||||
|
{
|
||||||
|
if (s->should_log(incoming_log_msg.level))
|
||||||
|
{
|
||||||
|
s->log(incoming_log_msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SPDLOG_CATCH_AND_HANDLE
|
||||||
|
|
||||||
|
if (should_flush_(incoming_log_msg))
|
||||||
|
{
|
||||||
|
backend_flush_();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void spdlog::async_logger::backend_flush_()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
for (auto &sink : sinks_)
|
||||||
|
{
|
||||||
|
sink->flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SPDLOG_CATCH_AND_HANDLE
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::shared_ptr<spdlog::logger> spdlog::async_logger::clone(std::string new_name)
|
||||||
|
{
|
||||||
|
auto cloned = std::make_shared<spdlog::async_logger>(std::move(new_name), sinks_.begin(), sinks_.end(), thread_pool_, overflow_policy_);
|
||||||
|
|
||||||
|
cloned->set_level(this->level());
|
||||||
|
cloned->flush_on(this->flush_level());
|
||||||
|
cloned->set_error_handler(this->error_handler());
|
||||||
|
return std::move(cloned);
|
||||||
|
}
|
72
third_party/spdlog/include/spdlog/details/circular_q.h
vendored
Normal file
72
third_party/spdlog/include/spdlog/details/circular_q.h
vendored
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
//
|
||||||
|
// Copyright(c) 2018 Gabi Melman.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
|
||||||
|
// cirucal q view of std::vector.
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace details {
|
||||||
|
template<typename T>
|
||||||
|
class circular_q
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using item_type = T;
|
||||||
|
|
||||||
|
explicit circular_q(size_t max_items)
|
||||||
|
: max_items_(max_items + 1) // one item is reserved as marker for full q
|
||||||
|
, v_(max_items_)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// push back, overrun (oldest) item if no room left
|
||||||
|
void push_back(T &&item)
|
||||||
|
{
|
||||||
|
v_[tail_] = std::move(item);
|
||||||
|
tail_ = (tail_ + 1) % max_items_;
|
||||||
|
|
||||||
|
if (tail_ == head_) // overrun last item if full
|
||||||
|
{
|
||||||
|
head_ = (head_ + 1) % max_items_;
|
||||||
|
++overrun_counter_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pop item from front.
|
||||||
|
// If there are no elements in the container, the behavior is undefined.
|
||||||
|
void pop_front(T &popped_item)
|
||||||
|
{
|
||||||
|
popped_item = std::move(v_[head_]);
|
||||||
|
head_ = (head_ + 1) % max_items_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool empty()
|
||||||
|
{
|
||||||
|
return tail_ == head_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool full()
|
||||||
|
{
|
||||||
|
// head is ahead of the tail by 1
|
||||||
|
return ((tail_ + 1) % max_items_) == head_;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t overrun_counter() const
|
||||||
|
{
|
||||||
|
return overrun_counter_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
size_t max_items_;
|
||||||
|
typename std::vector<T>::size_type head_ = 0;
|
||||||
|
typename std::vector<T>::size_type tail_ = 0;
|
||||||
|
|
||||||
|
std::vector<T> v_;
|
||||||
|
|
||||||
|
size_t overrun_counter_ = 0;
|
||||||
|
};
|
||||||
|
} // namespace details
|
||||||
|
} // namespace spdlog
|
74
third_party/spdlog/include/spdlog/details/console_globals.h
vendored
Normal file
74
third_party/spdlog/include/spdlog/details/console_globals.h
vendored
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
#pragma once
|
||||||
|
//
|
||||||
|
// Copyright(c) 2018 Gabi Melman.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "spdlog/details/null_mutex.h"
|
||||||
|
#include <cstdio>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
#ifndef NOMINMAX
|
||||||
|
#define NOMINMAX // prevent windows redefining min/max
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef WIN32_LEAN_AND_MEAN
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace details {
|
||||||
|
struct console_stdout
|
||||||
|
{
|
||||||
|
static std::FILE *stream()
|
||||||
|
{
|
||||||
|
return stdout;
|
||||||
|
}
|
||||||
|
#ifdef _WIN32
|
||||||
|
static HANDLE handle()
|
||||||
|
{
|
||||||
|
return ::GetStdHandle(STD_OUTPUT_HANDLE);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
struct console_stderr
|
||||||
|
{
|
||||||
|
static std::FILE *stream()
|
||||||
|
{
|
||||||
|
return stderr;
|
||||||
|
}
|
||||||
|
#ifdef _WIN32
|
||||||
|
static HANDLE handle()
|
||||||
|
{
|
||||||
|
return ::GetStdHandle(STD_ERROR_HANDLE);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
struct console_mutex
|
||||||
|
{
|
||||||
|
using mutex_t = std::mutex;
|
||||||
|
static mutex_t &mutex()
|
||||||
|
{
|
||||||
|
static mutex_t s_mutex;
|
||||||
|
return s_mutex;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct console_nullmutex
|
||||||
|
{
|
||||||
|
using mutex_t = null_mutex;
|
||||||
|
static mutex_t &mutex()
|
||||||
|
{
|
||||||
|
static mutex_t s_mutex;
|
||||||
|
return s_mutex;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace details
|
||||||
|
} // namespace spdlog
|
152
third_party/spdlog/include/spdlog/details/file_helper.h
vendored
Normal file
152
third_party/spdlog/include/spdlog/details/file_helper.h
vendored
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
//
|
||||||
|
// Copyright(c) 2015 Gabi Melman.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Helper class for file sinks.
|
||||||
|
// When failing to open a file, retry several times(5) with a delay interval(10 ms).
|
||||||
|
// Throw spdlog_ex exception on errors.
|
||||||
|
|
||||||
|
#include "spdlog/details/log_msg.h"
|
||||||
|
#include "spdlog/details/os.h"
|
||||||
|
|
||||||
|
#include <cerrno>
|
||||||
|
#include <chrono>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <string>
|
||||||
|
#include <thread>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace details {
|
||||||
|
|
||||||
|
class file_helper
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
const int open_tries = 5;
|
||||||
|
const int open_interval = 10;
|
||||||
|
|
||||||
|
explicit file_helper() = default;
|
||||||
|
|
||||||
|
file_helper(const file_helper &) = delete;
|
||||||
|
file_helper &operator=(const file_helper &) = delete;
|
||||||
|
|
||||||
|
~file_helper()
|
||||||
|
{
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void open(const filename_t &fname, bool truncate = false)
|
||||||
|
{
|
||||||
|
close();
|
||||||
|
auto *mode = truncate ? SPDLOG_FILENAME_T("wb") : SPDLOG_FILENAME_T("ab");
|
||||||
|
_filename = fname;
|
||||||
|
for (int tries = 0; tries < open_tries; ++tries)
|
||||||
|
{
|
||||||
|
if (!os::fopen_s(&fd_, fname, mode))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
details::os::sleep_for_millis(open_interval);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw spdlog_ex("Failed opening file " + os::filename_to_str(_filename) + " for writing", errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
void reopen(bool truncate)
|
||||||
|
{
|
||||||
|
if (_filename.empty())
|
||||||
|
{
|
||||||
|
throw spdlog_ex("Failed re opening file - was not opened before");
|
||||||
|
}
|
||||||
|
open(_filename, truncate);
|
||||||
|
}
|
||||||
|
|
||||||
|
void flush()
|
||||||
|
{
|
||||||
|
std::fflush(fd_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void close()
|
||||||
|
{
|
||||||
|
if (fd_ != nullptr)
|
||||||
|
{
|
||||||
|
std::fclose(fd_);
|
||||||
|
fd_ = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void write(const fmt::memory_buffer &buf)
|
||||||
|
{
|
||||||
|
size_t msg_size = buf.size();
|
||||||
|
auto data = buf.data();
|
||||||
|
if (std::fwrite(data, 1, msg_size, fd_) != msg_size)
|
||||||
|
{
|
||||||
|
throw spdlog_ex("Failed writing to file " + os::filename_to_str(_filename), errno);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t size() const
|
||||||
|
{
|
||||||
|
if (fd_ == nullptr)
|
||||||
|
{
|
||||||
|
throw spdlog_ex("Cannot use size() on closed file " + os::filename_to_str(_filename));
|
||||||
|
}
|
||||||
|
return os::filesize(fd_);
|
||||||
|
}
|
||||||
|
|
||||||
|
const filename_t &filename() const
|
||||||
|
{
|
||||||
|
return _filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool file_exists(const filename_t &fname)
|
||||||
|
{
|
||||||
|
return os::file_exists(fname);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// return file path and its extension:
|
||||||
|
//
|
||||||
|
// "mylog.txt" => ("mylog", ".txt")
|
||||||
|
// "mylog" => ("mylog", "")
|
||||||
|
// "mylog." => ("mylog.", "")
|
||||||
|
// "/dir1/dir2/mylog.txt" => ("/dir1/dir2/mylog", ".txt")
|
||||||
|
//
|
||||||
|
// the starting dot in filenames is ignored (hidden files):
|
||||||
|
//
|
||||||
|
// ".mylog" => (".mylog". "")
|
||||||
|
// "my_folder/.mylog" => ("my_folder/.mylog", "")
|
||||||
|
// "my_folder/.mylog.txt" => ("my_folder/.mylog", ".txt")
|
||||||
|
static std::tuple<filename_t, filename_t> split_by_extension(const spdlog::filename_t &fname)
|
||||||
|
{
|
||||||
|
auto ext_index = fname.rfind('.');
|
||||||
|
|
||||||
|
// no valid extension found - return whole path and empty string as
|
||||||
|
// extension
|
||||||
|
if (ext_index == filename_t::npos || ext_index == 0 || ext_index == fname.size() - 1)
|
||||||
|
{
|
||||||
|
return std::make_tuple(fname, spdlog::filename_t());
|
||||||
|
}
|
||||||
|
|
||||||
|
// treat casese like "/etc/rc.d/somelogfile or "/abc/.hiddenfile"
|
||||||
|
auto folder_index = fname.rfind(details::os::folder_sep);
|
||||||
|
if (folder_index != filename_t::npos && folder_index >= ext_index - 1)
|
||||||
|
{
|
||||||
|
return std::make_tuple(fname, spdlog::filename_t());
|
||||||
|
}
|
||||||
|
|
||||||
|
// finally - return a valid base and extension tuple
|
||||||
|
return std::make_tuple(fname.substr(0, ext_index), fname.substr(ext_index));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::FILE *fd_{nullptr};
|
||||||
|
filename_t _filename;
|
||||||
|
};
|
||||||
|
} // namespace details
|
||||||
|
} // namespace spdlog
|
122
third_party/spdlog/include/spdlog/details/fmt_helper.h
vendored
Normal file
122
third_party/spdlog/include/spdlog/details/fmt_helper.h
vendored
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
//
|
||||||
|
// Created by gabi on 6/15/18.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <type_traits>
|
||||||
|
#include "spdlog/fmt/fmt.h"
|
||||||
|
|
||||||
|
// Some fmt helpers to efficiently format and pad ints and strings
|
||||||
|
namespace spdlog {
|
||||||
|
namespace details {
|
||||||
|
namespace fmt_helper {
|
||||||
|
|
||||||
|
template<size_t Buffer_Size>
|
||||||
|
inline spdlog::string_view_t to_string_view(const fmt::basic_memory_buffer<char, Buffer_Size> &buf) SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
return spdlog::string_view_t(buf.data(), buf.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
template<size_t Buffer_Size1, size_t Buffer_Size2>
|
||||||
|
inline void append_buf(const fmt::basic_memory_buffer<char, Buffer_Size1> &buf, fmt::basic_memory_buffer<char, Buffer_Size2> &dest)
|
||||||
|
{
|
||||||
|
auto *buf_ptr = buf.data();
|
||||||
|
dest.append(buf_ptr, buf_ptr + buf.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
template<size_t Buffer_Size>
|
||||||
|
inline void append_string_view(spdlog::string_view_t view, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
|
||||||
|
{
|
||||||
|
auto *buf_ptr = view.data();
|
||||||
|
if (buf_ptr != nullptr)
|
||||||
|
{
|
||||||
|
dest.append(buf_ptr, buf_ptr + view.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, size_t Buffer_Size>
|
||||||
|
inline void append_int(T n, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
|
||||||
|
{
|
||||||
|
fmt::format_int i(n);
|
||||||
|
dest.append(i.data(), i.data() + i.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline unsigned count_digits(T n)
|
||||||
|
{
|
||||||
|
using count_type = typename std::conditional<(sizeof(T) > sizeof(uint32_t)), uint64_t, uint32_t>::type;
|
||||||
|
return static_cast<unsigned>(fmt::internal::count_digits(static_cast<count_type>(n)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<size_t Buffer_Size>
|
||||||
|
inline void pad2(int n, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
|
||||||
|
{
|
||||||
|
if (n > 99)
|
||||||
|
{
|
||||||
|
append_int(n, dest);
|
||||||
|
}
|
||||||
|
else if (n > 9) // 10-99
|
||||||
|
{
|
||||||
|
dest.push_back(static_cast<char>('0' + n / 10));
|
||||||
|
dest.push_back(static_cast<char>('0' + n % 10));
|
||||||
|
}
|
||||||
|
else if (n >= 0) // 0-9
|
||||||
|
{
|
||||||
|
dest.push_back('0');
|
||||||
|
dest.push_back(static_cast<char>('0' + n));
|
||||||
|
}
|
||||||
|
else // negatives (unlikely, but just in case, let fmt deal with it)
|
||||||
|
{
|
||||||
|
fmt::format_to(dest, "{:02}", n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, size_t Buffer_Size>
|
||||||
|
inline void pad_uint(T n, unsigned int width, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
|
||||||
|
{
|
||||||
|
static_assert(std::is_unsigned<T>::value, "pad_uint must get unsigned T");
|
||||||
|
auto digits = count_digits(n);
|
||||||
|
if (width > digits)
|
||||||
|
{
|
||||||
|
const char *zeroes = "0000000000000000000";
|
||||||
|
dest.append(zeroes, zeroes + width - digits);
|
||||||
|
}
|
||||||
|
append_int(n, dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, size_t Buffer_Size>
|
||||||
|
inline void pad3(T n, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
|
||||||
|
{
|
||||||
|
pad_uint(n, 3, dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, size_t Buffer_Size>
|
||||||
|
inline void pad6(T n, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
|
||||||
|
{
|
||||||
|
pad_uint(n, 6, dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, size_t Buffer_Size>
|
||||||
|
inline void pad9(T n, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
|
||||||
|
{
|
||||||
|
pad_uint(n, 9, dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
// return fraction of a second of the given time_point.
|
||||||
|
// e.g.
|
||||||
|
// fraction<std::milliseconds>(tp) -> will return the millis part of the second
|
||||||
|
template<typename ToDuration>
|
||||||
|
inline ToDuration time_fraction(const log_clock::time_point &tp)
|
||||||
|
{
|
||||||
|
using std::chrono::duration_cast;
|
||||||
|
using std::chrono::seconds;
|
||||||
|
auto duration = tp.time_since_epoch();
|
||||||
|
auto secs = duration_cast<seconds>(duration);
|
||||||
|
return duration_cast<ToDuration>(duration) - duration_cast<ToDuration>(secs);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace fmt_helper
|
||||||
|
} // namespace details
|
||||||
|
} // namespace spdlog
|
55
third_party/spdlog/include/spdlog/details/log_msg.h
vendored
Normal file
55
third_party/spdlog/include/spdlog/details/log_msg.h
vendored
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
//
|
||||||
|
// Copyright(c) 2015 Gabi Melman.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "spdlog/common.h"
|
||||||
|
#include "spdlog/details/os.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace details {
|
||||||
|
struct log_msg
|
||||||
|
{
|
||||||
|
|
||||||
|
log_msg(source_loc loc, const std::string *loggers_name, level::level_enum lvl, string_view_t view)
|
||||||
|
: logger_name(loggers_name)
|
||||||
|
, level(lvl)
|
||||||
|
#ifndef SPDLOG_NO_DATETIME
|
||||||
|
, time(os::now())
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SPDLOG_NO_THREAD_ID
|
||||||
|
, thread_id(os::thread_id())
|
||||||
|
#endif
|
||||||
|
, source(loc)
|
||||||
|
, payload(view)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
log_msg(const std::string *loggers_name, level::level_enum lvl, string_view_t view)
|
||||||
|
: log_msg(source_loc{}, loggers_name, lvl, view)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
log_msg(const log_msg &other) = default;
|
||||||
|
|
||||||
|
const std::string *logger_name{nullptr};
|
||||||
|
level::level_enum level{level::off};
|
||||||
|
log_clock::time_point time;
|
||||||
|
size_t thread_id{0};
|
||||||
|
size_t msg_id{0};
|
||||||
|
|
||||||
|
// wrapping the formatted text with color (updated by pattern_formatter).
|
||||||
|
mutable size_t color_range_start{0};
|
||||||
|
mutable size_t color_range_end{0};
|
||||||
|
|
||||||
|
source_loc source;
|
||||||
|
const string_view_t payload;
|
||||||
|
};
|
||||||
|
} // namespace details
|
||||||
|
} // namespace spdlog
|
441
third_party/spdlog/include/spdlog/details/logger_impl.h
vendored
Normal file
441
third_party/spdlog/include/spdlog/details/logger_impl.h
vendored
Normal file
@ -0,0 +1,441 @@
|
|||||||
|
//
|
||||||
|
// Copyright(c) 2015 Gabi Melman.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "spdlog/details/fmt_helper.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#define SPDLOG_CATCH_AND_HANDLE \
|
||||||
|
catch (const std::exception &ex) \
|
||||||
|
{ \
|
||||||
|
err_handler_(ex.what()); \
|
||||||
|
} \
|
||||||
|
catch (...) \
|
||||||
|
{ \
|
||||||
|
err_handler_("Unknown exception in logger"); \
|
||||||
|
}
|
||||||
|
|
||||||
|
// create logger with given name, sinks and the default pattern formatter
|
||||||
|
// all other ctors will call this one
|
||||||
|
template<typename It>
|
||||||
|
inline spdlog::logger::logger(std::string logger_name, It begin, It end)
|
||||||
|
: name_(std::move(logger_name))
|
||||||
|
, sinks_(begin, end)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// ctor with sinks as init list
|
||||||
|
inline spdlog::logger::logger(std::string logger_name, sinks_init_list sinks_list)
|
||||||
|
: logger(std::move(logger_name), sinks_list.begin(), sinks_list.end())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// ctor with single sink
|
||||||
|
inline spdlog::logger::logger(std::string logger_name, spdlog::sink_ptr single_sink)
|
||||||
|
: logger(std::move(logger_name), {std::move(single_sink)})
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
inline spdlog::logger::~logger() = default;
|
||||||
|
|
||||||
|
inline void spdlog::logger::set_formatter(std::unique_ptr<spdlog::formatter> f)
|
||||||
|
{
|
||||||
|
for (auto &sink : sinks_)
|
||||||
|
{
|
||||||
|
sink->set_formatter(f->clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void spdlog::logger::set_pattern(std::string pattern, pattern_time_type time_type)
|
||||||
|
{
|
||||||
|
auto new_formatter = details::make_unique<spdlog::pattern_formatter>(std::move(pattern), time_type);
|
||||||
|
set_formatter(std::move(new_formatter));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
inline void spdlog::logger::log(source_loc source, level::level_enum lvl, const char *fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
if (!should_log(lvl))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using details::fmt_helper::to_string_view;
|
||||||
|
fmt::memory_buffer buf;
|
||||||
|
fmt::format_to(buf, fmt, args...);
|
||||||
|
details::log_msg log_msg(source, &name_, lvl, to_string_view(buf));
|
||||||
|
sink_it_(log_msg);
|
||||||
|
}
|
||||||
|
SPDLOG_CATCH_AND_HANDLE
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
inline void spdlog::logger::log(level::level_enum lvl, const char *fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
log(source_loc{}, lvl, fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void spdlog::logger::log(source_loc source, level::level_enum lvl, const char *msg)
|
||||||
|
{
|
||||||
|
if (!should_log(lvl))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
details::log_msg log_msg(source, &name_, lvl, spdlog::string_view_t(msg));
|
||||||
|
sink_it_(log_msg);
|
||||||
|
}
|
||||||
|
SPDLOG_CATCH_AND_HANDLE
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void spdlog::logger::log(level::level_enum lvl, const char *msg)
|
||||||
|
{
|
||||||
|
log(source_loc{}, lvl, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T, typename std::enable_if<std::is_convertible<T, spdlog::string_view_t>::value, T>::type *>
|
||||||
|
inline void spdlog::logger::log(source_loc source, level::level_enum lvl, const T &msg)
|
||||||
|
{
|
||||||
|
if (!should_log(lvl))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
details::log_msg log_msg(source, &name_, lvl, msg);
|
||||||
|
sink_it_(log_msg);
|
||||||
|
}
|
||||||
|
SPDLOG_CATCH_AND_HANDLE
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T, typename std::enable_if<std::is_convertible<T, spdlog::string_view_t>::value, T>::type *>
|
||||||
|
inline void spdlog::logger::log(level::level_enum lvl, const T &msg)
|
||||||
|
{
|
||||||
|
log(source_loc{}, lvl, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T, typename std::enable_if<!std::is_convertible<T, spdlog::string_view_t>::value, T>::type *>
|
||||||
|
inline void spdlog::logger::log(source_loc source, level::level_enum lvl, const T &msg)
|
||||||
|
{
|
||||||
|
if (!should_log(lvl))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using details::fmt_helper::to_string_view;
|
||||||
|
fmt::memory_buffer buf;
|
||||||
|
fmt::format_to(buf, "{}", msg);
|
||||||
|
details::log_msg log_msg(source, &name_, lvl, to_string_view(buf));
|
||||||
|
sink_it_(log_msg);
|
||||||
|
}
|
||||||
|
SPDLOG_CATCH_AND_HANDLE
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T, typename std::enable_if<!std::is_convertible<T, spdlog::string_view_t>::value, T>::type *>
|
||||||
|
inline void spdlog::logger::log(level::level_enum lvl, const T &msg)
|
||||||
|
{
|
||||||
|
log(source_loc{}, lvl, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
inline void spdlog::logger::trace(const char *fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
log(level::trace, fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
inline void spdlog::logger::debug(const char *fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
log(level::debug, fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
inline void spdlog::logger::info(const char *fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
log(level::info, fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
inline void spdlog::logger::warn(const char *fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
log(level::warn, fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
inline void spdlog::logger::error(const char *fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
log(level::err, fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
inline void spdlog::logger::critical(const char *fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
log(level::critical, fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline void spdlog::logger::trace(const T &msg)
|
||||||
|
{
|
||||||
|
log(level::trace, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline void spdlog::logger::debug(const T &msg)
|
||||||
|
{
|
||||||
|
log(level::debug, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline void spdlog::logger::info(const T &msg)
|
||||||
|
{
|
||||||
|
log(level::info, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline void spdlog::logger::warn(const T &msg)
|
||||||
|
{
|
||||||
|
log(level::warn, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline void spdlog::logger::error(const T &msg)
|
||||||
|
{
|
||||||
|
log(level::err, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline void spdlog::logger::critical(const T &msg)
|
||||||
|
{
|
||||||
|
log(level::critical, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
||||||
|
|
||||||
|
inline void wbuf_to_utf8buf(const fmt::wmemory_buffer &wbuf, fmt::memory_buffer &target)
|
||||||
|
{
|
||||||
|
int wbuf_size = static_cast<int>(wbuf.size());
|
||||||
|
if (wbuf_size == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto result_size = ::WideCharToMultiByte(CP_UTF8, 0, wbuf.data(), wbuf_size, NULL, 0, NULL, NULL);
|
||||||
|
|
||||||
|
if (result_size > 0)
|
||||||
|
{
|
||||||
|
target.resize(result_size);
|
||||||
|
::WideCharToMultiByte(CP_UTF8, 0, wbuf.data(), wbuf_size, &target.data()[0], result_size, NULL, NULL);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw spdlog::spdlog_ex(fmt::format("WideCharToMultiByte failed. Last error: {}", ::GetLastError()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
inline void spdlog::logger::log(source_loc source, level::level_enum lvl, const wchar_t *fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
if (!should_log(lvl))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// format to wmemory_buffer and convert to utf8
|
||||||
|
using details::fmt_helper::to_string_view;
|
||||||
|
fmt::wmemory_buffer wbuf;
|
||||||
|
fmt::format_to(wbuf, fmt, args...);
|
||||||
|
fmt::memory_buffer buf;
|
||||||
|
wbuf_to_utf8buf(wbuf, buf);
|
||||||
|
details::log_msg log_msg(source, &name_, lvl, to_string_view(buf));
|
||||||
|
sink_it_(log_msg);
|
||||||
|
}
|
||||||
|
SPDLOG_CATCH_AND_HANDLE
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
inline void spdlog::logger::log(level::level_enum lvl, const wchar_t *fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
log(source_loc{}, lvl, fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
inline void spdlog::logger::trace(const wchar_t *fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
log(level::trace, fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
inline void spdlog::logger::debug(const wchar_t *fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
log(level::debug, fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
inline void spdlog::logger::info(const wchar_t *fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
log(level::info, fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
inline void spdlog::logger::warn(const wchar_t *fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
log(level::warn, fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
inline void spdlog::logger::error(const wchar_t *fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
log(level::err, fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
inline void spdlog::logger::critical(const wchar_t *fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
log(level::critical, fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
||||||
|
|
||||||
|
//
|
||||||
|
// name and level
|
||||||
|
//
|
||||||
|
inline const std::string &spdlog::logger::name() const
|
||||||
|
{
|
||||||
|
return name_;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void spdlog::logger::set_level(spdlog::level::level_enum log_level)
|
||||||
|
{
|
||||||
|
level_.store(log_level);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void spdlog::logger::set_error_handler(spdlog::log_err_handler err_handler)
|
||||||
|
{
|
||||||
|
err_handler_ = std::move(err_handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline spdlog::log_err_handler spdlog::logger::error_handler() const
|
||||||
|
{
|
||||||
|
return err_handler_;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void spdlog::logger::flush()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
flush_();
|
||||||
|
}
|
||||||
|
SPDLOG_CATCH_AND_HANDLE
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void spdlog::logger::flush_on(level::level_enum log_level)
|
||||||
|
{
|
||||||
|
flush_level_.store(log_level);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline spdlog::level::level_enum spdlog::logger::flush_level() const
|
||||||
|
{
|
||||||
|
return static_cast<spdlog::level::level_enum>(flush_level_.load(std::memory_order_relaxed));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool spdlog::logger::should_flush_(const details::log_msg &msg)
|
||||||
|
{
|
||||||
|
auto flush_level = flush_level_.load(std::memory_order_relaxed);
|
||||||
|
return (msg.level >= flush_level) && (msg.level != level::off);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline spdlog::level::level_enum spdlog::logger::default_level()
|
||||||
|
{
|
||||||
|
return static_cast<spdlog::level::level_enum>(SPDLOG_ACTIVE_LEVEL);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline spdlog::level::level_enum spdlog::logger::level() const
|
||||||
|
{
|
||||||
|
return static_cast<spdlog::level::level_enum>(level_.load(std::memory_order_relaxed));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool spdlog::logger::should_log(spdlog::level::level_enum msg_level) const
|
||||||
|
{
|
||||||
|
return msg_level >= level_.load(std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// protected virtual called at end of each user log call (if enabled) by the
|
||||||
|
// line_logger
|
||||||
|
//
|
||||||
|
inline void spdlog::logger::sink_it_(details::log_msg &msg)
|
||||||
|
{
|
||||||
|
#if defined(SPDLOG_ENABLE_MESSAGE_COUNTER)
|
||||||
|
incr_msg_counter_(msg);
|
||||||
|
#endif
|
||||||
|
for (auto &sink : sinks_)
|
||||||
|
{
|
||||||
|
if (sink->should_log(msg.level))
|
||||||
|
{
|
||||||
|
sink->log(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (should_flush_(msg))
|
||||||
|
{
|
||||||
|
flush_();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void spdlog::logger::flush_()
|
||||||
|
{
|
||||||
|
for (auto &sink : sinks_)
|
||||||
|
{
|
||||||
|
sink->flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void spdlog::logger::default_err_handler_(const std::string &msg)
|
||||||
|
{
|
||||||
|
auto now = time(nullptr);
|
||||||
|
if (now - last_err_time_ < 60)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
last_err_time_ = now;
|
||||||
|
auto tm_time = details::os::localtime(now);
|
||||||
|
char date_buf[100];
|
||||||
|
std::strftime(date_buf, sizeof(date_buf), "%Y-%m-%d %H:%M:%S", &tm_time);
|
||||||
|
fmt::print(stderr, "[*** LOG ERROR ***] [{}] [{}] {}\n", date_buf, name(), msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void spdlog::logger::incr_msg_counter_(details::log_msg &msg)
|
||||||
|
{
|
||||||
|
msg.msg_id = msg_counter_.fetch_add(1, std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const std::vector<spdlog::sink_ptr> &spdlog::logger::sinks() const
|
||||||
|
{
|
||||||
|
return sinks_;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::vector<spdlog::sink_ptr> &spdlog::logger::sinks()
|
||||||
|
{
|
||||||
|
return sinks_;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::shared_ptr<spdlog::logger> spdlog::logger::clone(std::string logger_name)
|
||||||
|
{
|
||||||
|
auto cloned = std::make_shared<spdlog::logger>(std::move(logger_name), sinks_.begin(), sinks_.end());
|
||||||
|
cloned->set_level(this->level());
|
||||||
|
cloned->flush_on(this->flush_level());
|
||||||
|
cloned->set_error_handler(this->error_handler());
|
||||||
|
return cloned;
|
||||||
|
}
|
121
third_party/spdlog/include/spdlog/details/mpmc_blocking_q.h
vendored
Normal file
121
third_party/spdlog/include/spdlog/details/mpmc_blocking_q.h
vendored
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
//
|
||||||
|
// Copyright(c) 2018 Gabi Melman.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
|
||||||
|
// multi producer-multi consumer blocking queue.
|
||||||
|
// enqueue(..) - will block until room found to put the new message.
|
||||||
|
// enqueue_nowait(..) - will return immediately with false if no room left in
|
||||||
|
// the queue.
|
||||||
|
// dequeue_for(..) - will block until the queue is not empty or timeout have
|
||||||
|
// passed.
|
||||||
|
|
||||||
|
#include "spdlog/details/circular_q.h"
|
||||||
|
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace details {
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
class mpmc_blocking_queue
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using item_type = T;
|
||||||
|
explicit mpmc_blocking_queue(size_t max_items)
|
||||||
|
: q_(max_items)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef __MINGW32__
|
||||||
|
// try to enqueue and block if no room left
|
||||||
|
void enqueue(T &&item)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(queue_mutex_);
|
||||||
|
pop_cv_.wait(lock, [this] { return !this->q_.full(); });
|
||||||
|
q_.push_back(std::move(item));
|
||||||
|
}
|
||||||
|
push_cv_.notify_one();
|
||||||
|
}
|
||||||
|
|
||||||
|
// enqueue immediately. overrun oldest message in the queue if no room left.
|
||||||
|
void enqueue_nowait(T &&item)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(queue_mutex_);
|
||||||
|
q_.push_back(std::move(item));
|
||||||
|
}
|
||||||
|
push_cv_.notify_one();
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to dequeue item. if no item found. wait upto timeout and try again
|
||||||
|
// Return true, if succeeded dequeue item, false otherwise
|
||||||
|
bool dequeue_for(T &popped_item, std::chrono::milliseconds wait_duration)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(queue_mutex_);
|
||||||
|
if (!push_cv_.wait_for(lock, wait_duration, [this] { return !this->q_.empty(); }))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
q_.pop_front(popped_item);
|
||||||
|
}
|
||||||
|
pop_cv_.notify_one();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
// apparently mingw deadlocks if the mutex is released before cv.notify_one(),
|
||||||
|
// so release the mutex at the very end each function.
|
||||||
|
|
||||||
|
// try to enqueue and block if no room left
|
||||||
|
void enqueue(T &&item)
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(queue_mutex_);
|
||||||
|
pop_cv_.wait(lock, [this] { return !this->q_.full(); });
|
||||||
|
q_.push_back(std::move(item));
|
||||||
|
push_cv_.notify_one();
|
||||||
|
}
|
||||||
|
|
||||||
|
// enqueue immediately. overrun oldest message in the queue if no room left.
|
||||||
|
void enqueue_nowait(T &&item)
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(queue_mutex_);
|
||||||
|
q_.push_back(std::move(item));
|
||||||
|
push_cv_.notify_one();
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to dequeue item. if no item found. wait upto timeout and try again
|
||||||
|
// Return true, if succeeded dequeue item, false otherwise
|
||||||
|
bool dequeue_for(T &popped_item, std::chrono::milliseconds wait_duration)
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(queue_mutex_);
|
||||||
|
if (!push_cv_.wait_for(lock, wait_duration, [this] { return !this->q_.empty(); }))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
q_.pop_front(popped_item);
|
||||||
|
pop_cv_.notify_one();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
size_t overrun_counter()
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(queue_mutex_);
|
||||||
|
return q_.overrun_counter();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::mutex queue_mutex_;
|
||||||
|
std::condition_variable push_cv_;
|
||||||
|
std::condition_variable pop_cv_;
|
||||||
|
spdlog::details::circular_q<T> q_;
|
||||||
|
};
|
||||||
|
} // namespace details
|
||||||
|
} // namespace spdlog
|
45
third_party/spdlog/include/spdlog/details/null_mutex.h
vendored
Normal file
45
third_party/spdlog/include/spdlog/details/null_mutex.h
vendored
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
//
|
||||||
|
// Copyright(c) 2015 Gabi Melman.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
// null, no cost dummy "mutex" and dummy "atomic" int
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace details {
|
||||||
|
struct null_mutex
|
||||||
|
{
|
||||||
|
void lock() {}
|
||||||
|
void unlock() {}
|
||||||
|
bool try_lock()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct null_atomic_int
|
||||||
|
{
|
||||||
|
int value;
|
||||||
|
null_atomic_int() = default;
|
||||||
|
|
||||||
|
explicit null_atomic_int(int val)
|
||||||
|
: value(val)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int load(std::memory_order) const
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void store(int val)
|
||||||
|
{
|
||||||
|
value = val;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace details
|
||||||
|
} // namespace spdlog
|
421
third_party/spdlog/include/spdlog/details/os.h
vendored
Normal file
421
third_party/spdlog/include/spdlog/details/os.h
vendored
Normal file
@ -0,0 +1,421 @@
|
|||||||
|
//
|
||||||
|
// Copyright(c) 2015 Gabi Melman.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../common.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <chrono>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
#include <ctime>
|
||||||
|
#include <functional>
|
||||||
|
#include <string>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
#ifndef NOMINMAX
|
||||||
|
#define NOMINMAX // prevent windows redefining min/max
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef WIN32_LEAN_AND_MEAN
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#endif
|
||||||
|
#include <io.h> // _get_osfhandle and _isatty support
|
||||||
|
#include <process.h> // _get_pid support
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#ifdef __MINGW32__
|
||||||
|
#include <share.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#else // unix
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
#include <sys/syscall.h> //Use gettid() syscall under linux to get thread id
|
||||||
|
|
||||||
|
#elif __FreeBSD__
|
||||||
|
#include <sys/thr.h> //Use thr_self() syscall under FreeBSD to get thread id
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // unix
|
||||||
|
|
||||||
|
#ifndef __has_feature // Clang - feature checking macros.
|
||||||
|
#define __has_feature(x) 0 // Compatibility with non-clang compilers.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace details {
|
||||||
|
namespace os {
|
||||||
|
|
||||||
|
inline spdlog::log_clock::time_point now() SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
|
||||||
|
#if defined __linux__ && defined SPDLOG_CLOCK_COARSE
|
||||||
|
timespec ts;
|
||||||
|
::clock_gettime(CLOCK_REALTIME_COARSE, &ts);
|
||||||
|
return std::chrono::time_point<log_clock, typename log_clock::duration>(
|
||||||
|
std::chrono::duration_cast<typename log_clock::duration>(std::chrono::seconds(ts.tv_sec) + std::chrono::nanoseconds(ts.tv_nsec)));
|
||||||
|
|
||||||
|
#else
|
||||||
|
return log_clock::now();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
inline std::tm localtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
std::tm tm;
|
||||||
|
localtime_s(&tm, &time_tt);
|
||||||
|
#else
|
||||||
|
std::tm tm;
|
||||||
|
localtime_r(&time_tt, &tm);
|
||||||
|
#endif
|
||||||
|
return tm;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::tm localtime() SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
std::time_t now_t = time(nullptr);
|
||||||
|
return localtime(now_t);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::tm gmtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
std::tm tm;
|
||||||
|
gmtime_s(&tm, &time_tt);
|
||||||
|
#else
|
||||||
|
std::tm tm;
|
||||||
|
gmtime_r(&time_tt, &tm);
|
||||||
|
#endif
|
||||||
|
return tm;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::tm gmtime() SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
std::time_t now_t = time(nullptr);
|
||||||
|
return gmtime(now_t);
|
||||||
|
}
|
||||||
|
|
||||||
|
// eol definition
|
||||||
|
#if !defined(SPDLOG_EOL)
|
||||||
|
#ifdef _WIN32
|
||||||
|
#define SPDLOG_EOL "\r\n"
|
||||||
|
#else
|
||||||
|
#define SPDLOG_EOL "\n"
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
SPDLOG_CONSTEXPR static const char *default_eol = SPDLOG_EOL;
|
||||||
|
|
||||||
|
// folder separator
|
||||||
|
#ifdef _WIN32
|
||||||
|
SPDLOG_CONSTEXPR static const char folder_sep = '\\';
|
||||||
|
#else
|
||||||
|
SPDLOG_CONSTEXPR static const char folder_sep = '/';
|
||||||
|
#endif
|
||||||
|
|
||||||
|
inline void prevent_child_fd(FILE *f)
|
||||||
|
{
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#if !defined(__cplusplus_winrt)
|
||||||
|
auto file_handle = (HANDLE)_get_osfhandle(_fileno(f));
|
||||||
|
if (!::SetHandleInformation(file_handle, HANDLE_FLAG_INHERIT, 0))
|
||||||
|
throw spdlog_ex("SetHandleInformation failed", errno);
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
auto fd = fileno(f);
|
||||||
|
if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
|
||||||
|
{
|
||||||
|
throw spdlog_ex("fcntl with FD_CLOEXEC failed", errno);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// fopen_s on non windows for writing
|
||||||
|
inline bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
#ifdef SPDLOG_WCHAR_FILENAMES
|
||||||
|
*fp = _wfsopen((filename.c_str()), mode.c_str(), _SH_DENYNO);
|
||||||
|
#else
|
||||||
|
*fp = _fsopen((filename.c_str()), mode.c_str(), _SH_DENYNO);
|
||||||
|
#endif
|
||||||
|
#else // unix
|
||||||
|
*fp = fopen((filename.c_str()), mode.c_str());
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef SPDLOG_PREVENT_CHILD_FD
|
||||||
|
if (*fp != nullptr)
|
||||||
|
{
|
||||||
|
prevent_child_fd(*fp);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return *fp == nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int remove(const filename_t &filename) SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
|
||||||
|
return _wremove(filename.c_str());
|
||||||
|
#else
|
||||||
|
return std::remove(filename.c_str());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int rename(const filename_t &filename1, const filename_t &filename2) SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
|
||||||
|
return _wrename(filename1.c_str(), filename2.c_str());
|
||||||
|
#else
|
||||||
|
return std::rename(filename1.c_str(), filename2.c_str());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return if file exists
|
||||||
|
inline bool file_exists(const filename_t &filename) SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
#ifdef SPDLOG_WCHAR_FILENAMES
|
||||||
|
auto attribs = GetFileAttributesW(filename.c_str());
|
||||||
|
#else
|
||||||
|
auto attribs = GetFileAttributesA(filename.c_str());
|
||||||
|
#endif
|
||||||
|
return (attribs != INVALID_FILE_ATTRIBUTES && !(attribs & FILE_ATTRIBUTE_DIRECTORY));
|
||||||
|
#else // common linux/unix all have the stat system call
|
||||||
|
struct stat buffer;
|
||||||
|
return (stat(filename.c_str(), &buffer) == 0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return file size according to open FILE* object
|
||||||
|
inline size_t filesize(FILE *f)
|
||||||
|
{
|
||||||
|
if (f == nullptr)
|
||||||
|
{
|
||||||
|
throw spdlog_ex("Failed getting file size. fd is null");
|
||||||
|
}
|
||||||
|
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||||
|
int fd = _fileno(f);
|
||||||
|
#if _WIN64 // 64 bits
|
||||||
|
__int64 ret = _filelengthi64(fd);
|
||||||
|
if (ret >= 0)
|
||||||
|
{
|
||||||
|
return static_cast<size_t>(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else // windows 32 bits
|
||||||
|
long ret = _filelength(fd);
|
||||||
|
if (ret >= 0)
|
||||||
|
{
|
||||||
|
return static_cast<size_t>(ret);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#else // unix
|
||||||
|
int fd = fileno(f);
|
||||||
|
// 64 bits(but not in osx or cygwin, where fstat64 is deprecated)
|
||||||
|
#if !defined(__FreeBSD__) && !defined(__APPLE__) && (defined(__x86_64__) || defined(__ppc64__)) && !defined(__CYGWIN__)
|
||||||
|
struct stat64 st;
|
||||||
|
if (fstat64(fd, &st) == 0)
|
||||||
|
{
|
||||||
|
return static_cast<size_t>(st.st_size);
|
||||||
|
}
|
||||||
|
#else // unix 32 bits or cygwin
|
||||||
|
struct stat st;
|
||||||
|
|
||||||
|
if (fstat(fd, &st) == 0)
|
||||||
|
{
|
||||||
|
return static_cast<size_t>(st.st_size);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
throw spdlog_ex("Failed getting file size from fd", errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return utc offset in minutes or throw spdlog_ex on failure
|
||||||
|
inline int utc_minutes_offset(const std::tm &tm = details::os::localtime())
|
||||||
|
{
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#if _WIN32_WINNT < _WIN32_WINNT_WS08
|
||||||
|
TIME_ZONE_INFORMATION tzinfo;
|
||||||
|
auto rv = GetTimeZoneInformation(&tzinfo);
|
||||||
|
#else
|
||||||
|
DYNAMIC_TIME_ZONE_INFORMATION tzinfo;
|
||||||
|
auto rv = GetDynamicTimeZoneInformation(&tzinfo);
|
||||||
|
#endif
|
||||||
|
if (rv == TIME_ZONE_ID_INVALID)
|
||||||
|
throw spdlog::spdlog_ex("Failed getting timezone info. ", errno);
|
||||||
|
|
||||||
|
int offset = -tzinfo.Bias;
|
||||||
|
if (tm.tm_isdst)
|
||||||
|
{
|
||||||
|
offset -= tzinfo.DaylightBias;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
offset -= tzinfo.StandardBias;
|
||||||
|
}
|
||||||
|
return offset;
|
||||||
|
#else
|
||||||
|
|
||||||
|
#if defined(sun) || defined(__sun) || defined(_AIX)
|
||||||
|
// 'tm_gmtoff' field is BSD extension and it's missing on SunOS/Solaris
|
||||||
|
struct helper
|
||||||
|
{
|
||||||
|
static long int calculate_gmt_offset(const std::tm &localtm = details::os::localtime(), const std::tm &gmtm = details::os::gmtime())
|
||||||
|
{
|
||||||
|
int local_year = localtm.tm_year + (1900 - 1);
|
||||||
|
int gmt_year = gmtm.tm_year + (1900 - 1);
|
||||||
|
|
||||||
|
long int days = (
|
||||||
|
// difference in day of year
|
||||||
|
localtm.tm_yday -
|
||||||
|
gmtm.tm_yday
|
||||||
|
|
||||||
|
// + intervening leap days
|
||||||
|
+ ((local_year >> 2) - (gmt_year >> 2)) - (local_year / 100 - gmt_year / 100) +
|
||||||
|
((local_year / 100 >> 2) - (gmt_year / 100 >> 2))
|
||||||
|
|
||||||
|
// + difference in years * 365 */
|
||||||
|
+ (long int)(local_year - gmt_year) * 365);
|
||||||
|
|
||||||
|
long int hours = (24 * days) + (localtm.tm_hour - gmtm.tm_hour);
|
||||||
|
long int mins = (60 * hours) + (localtm.tm_min - gmtm.tm_min);
|
||||||
|
long int secs = (60 * mins) + (localtm.tm_sec - gmtm.tm_sec);
|
||||||
|
|
||||||
|
return secs;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto offset_seconds = helper::calculate_gmt_offset(tm);
|
||||||
|
#else
|
||||||
|
auto offset_seconds = tm.tm_gmtoff;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return static_cast<int>(offset_seconds / 60);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return current thread id as size_t
|
||||||
|
// It exists because the std::this_thread::get_id() is much slower(especially
|
||||||
|
// under VS 2013)
|
||||||
|
inline size_t _thread_id() SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
return static_cast<size_t>(::GetCurrentThreadId());
|
||||||
|
#elif __linux__
|
||||||
|
#if defined(__ANDROID__) && defined(__ANDROID_API__) && (__ANDROID_API__ < 21)
|
||||||
|
#define SYS_gettid __NR_gettid
|
||||||
|
#endif
|
||||||
|
return static_cast<size_t>(syscall(SYS_gettid));
|
||||||
|
#elif __FreeBSD__
|
||||||
|
long tid;
|
||||||
|
thr_self(&tid);
|
||||||
|
return static_cast<size_t>(tid);
|
||||||
|
#elif __APPLE__
|
||||||
|
uint64_t tid;
|
||||||
|
pthread_threadid_np(nullptr, &tid);
|
||||||
|
return static_cast<size_t>(tid);
|
||||||
|
#else // Default to standard C++11 (other Unix)
|
||||||
|
return static_cast<size_t>(std::hash<std::thread::id>()(std::this_thread::get_id()));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return current thread id as size_t (from thread local storage)
|
||||||
|
inline size_t thread_id() SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
#if defined(SPDLOG_NO_TLS)
|
||||||
|
return _thread_id();
|
||||||
|
#else // cache thread id in tls
|
||||||
|
static thread_local const size_t tid = _thread_id();
|
||||||
|
return tid;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is avoid msvc issue in sleep_for that happens if the clock changes.
|
||||||
|
// See https://github.com/gabime/spdlog/issues/609
|
||||||
|
inline void sleep_for_millis(int milliseconds) SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
#if defined(_WIN32)
|
||||||
|
::Sleep(milliseconds);
|
||||||
|
#else
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined)
|
||||||
|
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
|
||||||
|
#define SPDLOG_FILENAME_T(s) L##s
|
||||||
|
inline std::string filename_to_str(const filename_t &filename)
|
||||||
|
{
|
||||||
|
std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> c;
|
||||||
|
return c.to_bytes(filename);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#define SPDLOG_FILENAME_T(s) s
|
||||||
|
inline std::string filename_to_str(const filename_t &filename)
|
||||||
|
{
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
inline int pid()
|
||||||
|
{
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
return static_cast<int>(::GetCurrentProcessId());
|
||||||
|
#else
|
||||||
|
return static_cast<int>(::getpid());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine if the terminal supports colors
|
||||||
|
// Source: https://github.com/agauniyal/rang/
|
||||||
|
inline bool is_color_terminal() SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
static constexpr const char *Terms[] = {
|
||||||
|
"ansi", "color", "console", "cygwin", "gnome", "konsole", "kterm", "linux", "msys", "putty", "rxvt", "screen", "vt100", "xterm"};
|
||||||
|
|
||||||
|
const char *env_p = std::getenv("TERM");
|
||||||
|
if (env_p == nullptr)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const bool result =
|
||||||
|
std::any_of(std::begin(Terms), std::end(Terms), [&](const char *term) { return std::strstr(env_p, term) != nullptr; });
|
||||||
|
return result;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detrmine if the terminal attached
|
||||||
|
// Source: https://github.com/agauniyal/rang/
|
||||||
|
inline bool in_terminal(FILE *file) SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
return _isatty(_fileno(file)) != 0;
|
||||||
|
#else
|
||||||
|
return isatty(fileno(file)) != 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
} // namespace os
|
||||||
|
} // namespace details
|
||||||
|
} // namespace spdlog
|
1336
third_party/spdlog/include/spdlog/details/pattern_formatter.h
vendored
Normal file
1336
third_party/spdlog/include/spdlog/details/pattern_formatter.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
71
third_party/spdlog/include/spdlog/details/periodic_worker.h
vendored
Normal file
71
third_party/spdlog/include/spdlog/details/periodic_worker.h
vendored
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
|
||||||
|
//
|
||||||
|
// Copyright(c) 2018 Gabi Melman.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// periodic worker thread - periodically executes the given callback function.
|
||||||
|
//
|
||||||
|
// RAII over the owned thread:
|
||||||
|
// creates the thread on construction.
|
||||||
|
// stops and joins the thread on destruction (if the thread is executing a callback, wait for it to finish first).
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <functional>
|
||||||
|
#include <mutex>
|
||||||
|
#include <thread>
|
||||||
|
namespace spdlog {
|
||||||
|
namespace details {
|
||||||
|
|
||||||
|
class periodic_worker
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
periodic_worker(const std::function<void()> &callback_fun, std::chrono::seconds interval)
|
||||||
|
{
|
||||||
|
active_ = (interval > std::chrono::seconds::zero());
|
||||||
|
if (!active_)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
worker_thread_ = std::thread([this, callback_fun, interval]() {
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(this->mutex_);
|
||||||
|
if (this->cv_.wait_for(lock, interval, [this] { return !this->active_; }))
|
||||||
|
{
|
||||||
|
return; // active_ == false, so exit this thread
|
||||||
|
}
|
||||||
|
callback_fun();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
periodic_worker(const periodic_worker &) = delete;
|
||||||
|
periodic_worker &operator=(const periodic_worker &) = delete;
|
||||||
|
|
||||||
|
// stop the worker thread and join it
|
||||||
|
~periodic_worker()
|
||||||
|
{
|
||||||
|
if (worker_thread_.joinable())
|
||||||
|
{
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
active_ = false;
|
||||||
|
}
|
||||||
|
cv_.notify_one();
|
||||||
|
worker_thread_.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool active_;
|
||||||
|
std::thread worker_thread_;
|
||||||
|
std::mutex mutex_;
|
||||||
|
std::condition_variable cv_;
|
||||||
|
};
|
||||||
|
} // namespace details
|
||||||
|
} // namespace spdlog
|
285
third_party/spdlog/include/spdlog/details/registry.h
vendored
Normal file
285
third_party/spdlog/include/spdlog/details/registry.h
vendored
Normal file
@ -0,0 +1,285 @@
|
|||||||
|
//
|
||||||
|
// Copyright(c) 2015 Gabi Melman.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Loggers registy of unique name->logger pointer
|
||||||
|
// An attempt to create a logger with an already existing name will be ignored
|
||||||
|
// If user requests a non existing logger, nullptr will be returned
|
||||||
|
// This class is thread safe
|
||||||
|
|
||||||
|
#include "spdlog/common.h"
|
||||||
|
#include "spdlog/details/periodic_worker.h"
|
||||||
|
#include "spdlog/logger.h"
|
||||||
|
|
||||||
|
#ifndef SPDLOG_DISABLE_DEFAULT_LOGGER
|
||||||
|
// support for the default stdout color logger
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include "spdlog/sinks/wincolor_sink.h"
|
||||||
|
#else
|
||||||
|
#include "spdlog/sinks/ansicolor_sink.h"
|
||||||
|
#endif
|
||||||
|
#endif // SPDLOG_DISABLE_DEFAULT_LOGGER
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace details {
|
||||||
|
class thread_pool;
|
||||||
|
|
||||||
|
class registry
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
registry(const registry &) = delete;
|
||||||
|
registry &operator=(const registry &) = delete;
|
||||||
|
|
||||||
|
void register_logger(std::shared_ptr<logger> new_logger)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||||
|
register_logger_(std::move(new_logger));
|
||||||
|
}
|
||||||
|
|
||||||
|
void initialize_logger(std::shared_ptr<logger> new_logger)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||||
|
new_logger->set_formatter(formatter_->clone());
|
||||||
|
|
||||||
|
if (err_handler_)
|
||||||
|
{
|
||||||
|
new_logger->set_error_handler(err_handler_);
|
||||||
|
}
|
||||||
|
|
||||||
|
new_logger->set_level(level_);
|
||||||
|
new_logger->flush_on(flush_level_);
|
||||||
|
|
||||||
|
if (automatic_registration_)
|
||||||
|
{
|
||||||
|
register_logger_(std::move(new_logger));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<logger> get(const std::string &logger_name)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||||
|
auto found = loggers_.find(logger_name);
|
||||||
|
return found == loggers_.end() ? nullptr : found->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<logger> default_logger()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||||
|
return default_logger_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return raw ptr to the default logger.
|
||||||
|
// To be used directly by the spdlog default api (e.g. spdlog::info)
|
||||||
|
// This make the default API faster, but cannot be used concurrently with set_default_logger().
|
||||||
|
// e.g do not call set_default_logger() from one thread while calling spdlog::info() from another.
|
||||||
|
logger *get_default_raw()
|
||||||
|
{
|
||||||
|
return default_logger_.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
// set default logger.
|
||||||
|
// default logger is stored in default_logger_ (for faster retrieval) and in the loggers_ map.
|
||||||
|
void set_default_logger(std::shared_ptr<logger> new_default_logger)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||||
|
// remove previous default logger from the map
|
||||||
|
if (default_logger_ != nullptr)
|
||||||
|
{
|
||||||
|
loggers_.erase(default_logger_->name());
|
||||||
|
}
|
||||||
|
if (new_default_logger != nullptr)
|
||||||
|
{
|
||||||
|
loggers_[new_default_logger->name()] = new_default_logger;
|
||||||
|
}
|
||||||
|
default_logger_ = std::move(new_default_logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_tp(std::shared_ptr<thread_pool> tp)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> lock(tp_mutex_);
|
||||||
|
tp_ = std::move(tp);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<thread_pool> get_tp()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> lock(tp_mutex_);
|
||||||
|
return tp_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set global formatter. Each sink in each logger will get a clone of this object
|
||||||
|
void set_formatter(std::unique_ptr<formatter> formatter)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||||
|
formatter_ = std::move(formatter);
|
||||||
|
for (auto &l : loggers_)
|
||||||
|
{
|
||||||
|
l.second->set_formatter(formatter_->clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_level(level::level_enum log_level)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||||
|
for (auto &l : loggers_)
|
||||||
|
{
|
||||||
|
l.second->set_level(log_level);
|
||||||
|
}
|
||||||
|
level_ = log_level;
|
||||||
|
}
|
||||||
|
|
||||||
|
void flush_on(level::level_enum log_level)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||||
|
for (auto &l : loggers_)
|
||||||
|
{
|
||||||
|
l.second->flush_on(log_level);
|
||||||
|
}
|
||||||
|
flush_level_ = log_level;
|
||||||
|
}
|
||||||
|
|
||||||
|
void flush_every(std::chrono::seconds interval)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(flusher_mutex_);
|
||||||
|
std::function<void()> clbk = std::bind(®istry::flush_all, this);
|
||||||
|
periodic_flusher_ = details::make_unique<periodic_worker>(clbk, interval);
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_error_handler(log_err_handler handler)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||||
|
for (auto &l : loggers_)
|
||||||
|
{
|
||||||
|
l.second->set_error_handler(handler);
|
||||||
|
}
|
||||||
|
err_handler_ = handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
void apply_all(const std::function<void(const std::shared_ptr<logger>)> &fun)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||||
|
for (auto &l : loggers_)
|
||||||
|
{
|
||||||
|
fun(l.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void flush_all()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||||
|
for (auto &l : loggers_)
|
||||||
|
{
|
||||||
|
l.second->flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void drop(const std::string &logger_name)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||||
|
loggers_.erase(logger_name);
|
||||||
|
if (default_logger_ && default_logger_->name() == logger_name)
|
||||||
|
{
|
||||||
|
default_logger_.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void drop_all()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||||
|
loggers_.clear();
|
||||||
|
default_logger_.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
// clean all resources and threads started by the registry
|
||||||
|
void shutdown()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(flusher_mutex_);
|
||||||
|
periodic_flusher_.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
drop_all();
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> lock(tp_mutex_);
|
||||||
|
tp_.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::recursive_mutex &tp_mutex()
|
||||||
|
{
|
||||||
|
return tp_mutex_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_automatic_registration(bool automatic_regsistration)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||||
|
automatic_registration_ = automatic_regsistration;
|
||||||
|
}
|
||||||
|
|
||||||
|
static registry &instance()
|
||||||
|
{
|
||||||
|
static registry s_instance;
|
||||||
|
return s_instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
registry()
|
||||||
|
: formatter_(new pattern_formatter())
|
||||||
|
{
|
||||||
|
|
||||||
|
#ifndef SPDLOG_DISABLE_DEFAULT_LOGGER
|
||||||
|
// create default logger (ansicolor_stdout_sink_mt or wincolor_stdout_sink_mt in windows).
|
||||||
|
#ifdef _WIN32
|
||||||
|
auto color_sink = std::make_shared<sinks::wincolor_stdout_sink_mt>();
|
||||||
|
#else
|
||||||
|
auto color_sink = std::make_shared<sinks::ansicolor_stdout_sink_mt>();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const char *default_logger_name = "";
|
||||||
|
default_logger_ = std::make_shared<spdlog::logger>(default_logger_name, std::move(color_sink));
|
||||||
|
loggers_[default_logger_name] = default_logger_;
|
||||||
|
|
||||||
|
#endif // SPDLOG_DISABLE_DEFAULT_LOGGER
|
||||||
|
}
|
||||||
|
|
||||||
|
~registry() = default;
|
||||||
|
|
||||||
|
void throw_if_exists_(const std::string &logger_name)
|
||||||
|
{
|
||||||
|
if (loggers_.find(logger_name) != loggers_.end())
|
||||||
|
{
|
||||||
|
throw spdlog_ex("logger with name '" + logger_name + "' already exists");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void register_logger_(std::shared_ptr<logger> new_logger)
|
||||||
|
{
|
||||||
|
auto logger_name = new_logger->name();
|
||||||
|
throw_if_exists_(logger_name);
|
||||||
|
loggers_[logger_name] = std::move(new_logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::mutex logger_map_mutex_, flusher_mutex_;
|
||||||
|
std::recursive_mutex tp_mutex_;
|
||||||
|
std::unordered_map<std::string, std::shared_ptr<logger>> loggers_;
|
||||||
|
std::unique_ptr<formatter> formatter_;
|
||||||
|
level::level_enum level_ = spdlog::logger::default_level();
|
||||||
|
level::level_enum flush_level_ = level::off;
|
||||||
|
log_err_handler err_handler_;
|
||||||
|
std::shared_ptr<thread_pool> tp_;
|
||||||
|
std::unique_ptr<periodic_worker> periodic_flusher_;
|
||||||
|
std::shared_ptr<logger> default_logger_;
|
||||||
|
bool automatic_registration_ = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace details
|
||||||
|
} // namespace spdlog
|
238
third_party/spdlog/include/spdlog/details/thread_pool.h
vendored
Normal file
238
third_party/spdlog/include/spdlog/details/thread_pool.h
vendored
Normal file
@ -0,0 +1,238 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "spdlog/details/fmt_helper.h"
|
||||||
|
#include "spdlog/details/log_msg.h"
|
||||||
|
#include "spdlog/details/mpmc_blocking_q.h"
|
||||||
|
#include "spdlog/details/os.h"
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <memory>
|
||||||
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace details {
|
||||||
|
|
||||||
|
using async_logger_ptr = std::shared_ptr<spdlog::async_logger>;
|
||||||
|
|
||||||
|
enum class async_msg_type
|
||||||
|
{
|
||||||
|
log,
|
||||||
|
flush,
|
||||||
|
terminate
|
||||||
|
};
|
||||||
|
|
||||||
|
// Async msg to move to/from the queue
|
||||||
|
// Movable only. should never be copied
|
||||||
|
struct async_msg
|
||||||
|
{
|
||||||
|
async_msg_type msg_type;
|
||||||
|
level::level_enum level;
|
||||||
|
log_clock::time_point time;
|
||||||
|
size_t thread_id;
|
||||||
|
fmt::basic_memory_buffer<char, 176> raw;
|
||||||
|
|
||||||
|
size_t msg_id;
|
||||||
|
source_loc source;
|
||||||
|
async_logger_ptr worker_ptr;
|
||||||
|
|
||||||
|
async_msg() = default;
|
||||||
|
~async_msg() = default;
|
||||||
|
|
||||||
|
// should only be moved in or out of the queue..
|
||||||
|
async_msg(const async_msg &) = delete;
|
||||||
|
|
||||||
|
// support for vs2013 move
|
||||||
|
#if defined(_MSC_VER) && _MSC_VER <= 1800
|
||||||
|
async_msg(async_msg &&other) SPDLOG_NOEXCEPT : msg_type(other.msg_type),
|
||||||
|
level(other.level),
|
||||||
|
time(other.time),
|
||||||
|
thread_id(other.thread_id),
|
||||||
|
raw(move(other.raw)),
|
||||||
|
msg_id(other.msg_id),
|
||||||
|
source(other.source),
|
||||||
|
worker_ptr(std::move(other.worker_ptr))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
async_msg &operator=(async_msg &&other) SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
msg_type = other.msg_type;
|
||||||
|
level = other.level;
|
||||||
|
time = other.time;
|
||||||
|
thread_id = other.thread_id;
|
||||||
|
raw = std::move(other.raw);
|
||||||
|
msg_id = other.msg_id;
|
||||||
|
source = other.source;
|
||||||
|
worker_ptr = std::move(other.worker_ptr);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
#else // (_MSC_VER) && _MSC_VER <= 1800
|
||||||
|
async_msg(async_msg &&) = default;
|
||||||
|
async_msg &operator=(async_msg &&) = default;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// construct from log_msg with given type
|
||||||
|
async_msg(async_logger_ptr &&worker, async_msg_type the_type, details::log_msg &m)
|
||||||
|
: msg_type(the_type)
|
||||||
|
, level(m.level)
|
||||||
|
, time(m.time)
|
||||||
|
, thread_id(m.thread_id)
|
||||||
|
, msg_id(m.msg_id)
|
||||||
|
, source(m.source)
|
||||||
|
, worker_ptr(std::move(worker))
|
||||||
|
{
|
||||||
|
fmt_helper::append_string_view(m.payload, raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
async_msg(async_logger_ptr &&worker, async_msg_type the_type)
|
||||||
|
: msg_type(the_type)
|
||||||
|
, level(level::off)
|
||||||
|
, time()
|
||||||
|
, thread_id(0)
|
||||||
|
, msg_id(0)
|
||||||
|
, source()
|
||||||
|
, worker_ptr(std::move(worker))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit async_msg(async_msg_type the_type)
|
||||||
|
: async_msg(nullptr, the_type)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy into log_msg
|
||||||
|
log_msg to_log_msg()
|
||||||
|
{
|
||||||
|
log_msg msg(&worker_ptr->name(), level, string_view_t(raw.data(), raw.size()));
|
||||||
|
msg.time = time;
|
||||||
|
msg.thread_id = thread_id;
|
||||||
|
msg.msg_id = msg_id;
|
||||||
|
msg.source = source;
|
||||||
|
msg.color_range_start = 0;
|
||||||
|
msg.color_range_end = 0;
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class thread_pool
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using item_type = async_msg;
|
||||||
|
using q_type = details::mpmc_blocking_queue<item_type>;
|
||||||
|
|
||||||
|
thread_pool(size_t q_max_items, size_t threads_n)
|
||||||
|
: q_(q_max_items)
|
||||||
|
{
|
||||||
|
// std::cout << "thread_pool() q_size_bytes: " << q_size_bytes <<
|
||||||
|
// "\tthreads_n: " << threads_n << std::endl;
|
||||||
|
if (threads_n == 0 || threads_n > 1000)
|
||||||
|
{
|
||||||
|
throw spdlog_ex("spdlog::thread_pool(): invalid threads_n param (valid "
|
||||||
|
"range is 1-1000)");
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i < threads_n; i++)
|
||||||
|
{
|
||||||
|
threads_.emplace_back(&thread_pool::worker_loop_, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// message all threads to terminate gracefully join them
|
||||||
|
~thread_pool()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < threads_.size(); i++)
|
||||||
|
{
|
||||||
|
post_async_msg_(async_msg(async_msg_type::terminate), async_overflow_policy::block);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &t : threads_)
|
||||||
|
{
|
||||||
|
t.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
thread_pool(const thread_pool &) = delete;
|
||||||
|
thread_pool &operator=(thread_pool &&) = delete;
|
||||||
|
|
||||||
|
void post_log(async_logger_ptr &&worker_ptr, details::log_msg &msg, async_overflow_policy overflow_policy)
|
||||||
|
{
|
||||||
|
async_msg async_m(std::move(worker_ptr), async_msg_type::log, msg);
|
||||||
|
post_async_msg_(std::move(async_m), overflow_policy);
|
||||||
|
}
|
||||||
|
|
||||||
|
void post_flush(async_logger_ptr &&worker_ptr, async_overflow_policy overflow_policy)
|
||||||
|
{
|
||||||
|
post_async_msg_(async_msg(std::move(worker_ptr), async_msg_type::flush), overflow_policy);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t overrun_counter()
|
||||||
|
{
|
||||||
|
return q_.overrun_counter();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
q_type q_;
|
||||||
|
|
||||||
|
std::vector<std::thread> threads_;
|
||||||
|
|
||||||
|
void post_async_msg_(async_msg &&new_msg, async_overflow_policy overflow_policy)
|
||||||
|
{
|
||||||
|
if (overflow_policy == async_overflow_policy::block)
|
||||||
|
{
|
||||||
|
q_.enqueue(std::move(new_msg));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
q_.enqueue_nowait(std::move(new_msg));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void worker_loop_()
|
||||||
|
{
|
||||||
|
while (process_next_msg_()) {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// process next message in the queue
|
||||||
|
// return true if this thread should still be active (while no terminate msg
|
||||||
|
// was received)
|
||||||
|
bool process_next_msg_()
|
||||||
|
{
|
||||||
|
async_msg incoming_async_msg;
|
||||||
|
bool dequeued = q_.dequeue_for(incoming_async_msg, std::chrono::seconds(10));
|
||||||
|
if (!dequeued)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (incoming_async_msg.msg_type)
|
||||||
|
{
|
||||||
|
case async_msg_type::log:
|
||||||
|
{
|
||||||
|
auto msg = incoming_async_msg.to_log_msg();
|
||||||
|
incoming_async_msg.worker_ptr->backend_log_(msg);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case async_msg_type::flush:
|
||||||
|
{
|
||||||
|
incoming_async_msg.worker_ptr->backend_flush_();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
case async_msg_type::terminate:
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(false && "Unexpected async_msg_type");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace details
|
||||||
|
} // namespace spdlog
|
172
third_party/spdlog/include/spdlog/fmt/bin_to_hex.h
vendored
Normal file
172
third_party/spdlog/include/spdlog/fmt/bin_to_hex.h
vendored
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
//
|
||||||
|
// Copyright(c) 2015 Gabi Melman.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
//
|
||||||
|
// Support for logging binary data as hex
|
||||||
|
// format flags:
|
||||||
|
// {:X} - print in uppercase.
|
||||||
|
// {:s} - don't separate each byte with space.
|
||||||
|
// {:p} - don't print the position on each line start.
|
||||||
|
// {:n} - don't split the output to lines.
|
||||||
|
|
||||||
|
//
|
||||||
|
// Examples:
|
||||||
|
//
|
||||||
|
// std::vector<char> v(200, 0x0b);
|
||||||
|
// logger->info("Some buffer {}", spdlog::to_hex(v));
|
||||||
|
// char buf[128];
|
||||||
|
// logger->info("Some buffer {:X}", spdlog::to_hex(std::begin(buf), std::end(buf)));
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace details {
|
||||||
|
|
||||||
|
template<typename It>
|
||||||
|
class bytes_range
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
bytes_range(It range_begin, It range_end)
|
||||||
|
: begin_(range_begin)
|
||||||
|
, end_(range_end)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
It begin() const
|
||||||
|
{
|
||||||
|
return begin_;
|
||||||
|
}
|
||||||
|
It end() const
|
||||||
|
{
|
||||||
|
return end_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
It begin_, end_;
|
||||||
|
};
|
||||||
|
} // namespace details
|
||||||
|
|
||||||
|
// create a bytes_range that wraps the given container
|
||||||
|
template<typename Container>
|
||||||
|
inline details::bytes_range<typename Container::const_iterator> to_hex(const Container &container)
|
||||||
|
{
|
||||||
|
static_assert(sizeof(typename Container::value_type) == 1, "sizeof(Container::value_type) != 1");
|
||||||
|
using Iter = typename Container::const_iterator;
|
||||||
|
return details::bytes_range<Iter>(std::begin(container), std::end(container));
|
||||||
|
}
|
||||||
|
|
||||||
|
// create bytes_range from ranges
|
||||||
|
template<typename It>
|
||||||
|
inline details::bytes_range<It> to_hex(const It range_begin, const It range_end)
|
||||||
|
{
|
||||||
|
return details::bytes_range<It>(range_begin, range_end);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace spdlog
|
||||||
|
|
||||||
|
namespace fmt {
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct formatter<spdlog::details::bytes_range<T>>
|
||||||
|
{
|
||||||
|
const std::size_t line_size = 100;
|
||||||
|
const char delimiter = ' ';
|
||||||
|
|
||||||
|
bool put_newlines = true;
|
||||||
|
bool put_delimiters = true;
|
||||||
|
bool use_uppercase = false;
|
||||||
|
bool put_positions = true; // position on start of each line
|
||||||
|
|
||||||
|
// parse the format string flags
|
||||||
|
template<typename ParseContext>
|
||||||
|
auto parse(ParseContext &ctx) -> decltype(ctx.begin())
|
||||||
|
{
|
||||||
|
auto it = ctx.begin();
|
||||||
|
while (*it && *it != '}')
|
||||||
|
{
|
||||||
|
switch (*it)
|
||||||
|
{
|
||||||
|
case 'X':
|
||||||
|
use_uppercase = true;
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
put_delimiters = false;
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
put_positions = false;
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
put_newlines = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
// format the given bytes range as hex
|
||||||
|
template<typename FormatContext, typename Container>
|
||||||
|
auto format(const spdlog::details::bytes_range<Container> &the_range, FormatContext &ctx) -> decltype(ctx.out())
|
||||||
|
{
|
||||||
|
SPDLOG_CONSTEXPR const char *hex_upper = "0123456789ABCDEF";
|
||||||
|
SPDLOG_CONSTEXPR const char *hex_lower = "0123456789abcdef";
|
||||||
|
const char *hex_chars = use_uppercase ? hex_upper : hex_lower;
|
||||||
|
|
||||||
|
std::size_t pos = 0;
|
||||||
|
std::size_t column = line_size;
|
||||||
|
auto inserter = ctx.begin();
|
||||||
|
|
||||||
|
for (auto &item : the_range)
|
||||||
|
{
|
||||||
|
auto ch = static_cast<unsigned char>(item);
|
||||||
|
pos++;
|
||||||
|
|
||||||
|
if (put_newlines && column >= line_size)
|
||||||
|
{
|
||||||
|
column = put_newline(inserter, pos);
|
||||||
|
|
||||||
|
// put first byte without delimiter in front of it
|
||||||
|
*inserter++ = hex_chars[(ch >> 4) & 0x0f];
|
||||||
|
*inserter++ = hex_chars[ch & 0x0f];
|
||||||
|
column += 2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (put_delimiters)
|
||||||
|
{
|
||||||
|
*inserter++ = delimiter;
|
||||||
|
++column;
|
||||||
|
}
|
||||||
|
|
||||||
|
*inserter++ = hex_chars[(ch >> 4) & 0x0f];
|
||||||
|
*inserter++ = hex_chars[ch & 0x0f];
|
||||||
|
column += 2;
|
||||||
|
}
|
||||||
|
return inserter;
|
||||||
|
}
|
||||||
|
|
||||||
|
// put newline(and position header)
|
||||||
|
// return the next column
|
||||||
|
template<typename It>
|
||||||
|
std::size_t put_newline(It inserter, std::size_t pos)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
*inserter++ = '\r';
|
||||||
|
#endif
|
||||||
|
*inserter++ = '\n';
|
||||||
|
|
||||||
|
if (put_positions)
|
||||||
|
{
|
||||||
|
fmt::format_to(inserter, "{:<04X}: ", pos - 1);
|
||||||
|
return 7;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace fmt
|
23
third_party/spdlog/include/spdlog/fmt/bundled/LICENSE.rst
vendored
Normal file
23
third_party/spdlog/include/spdlog/fmt/bundled/LICENSE.rst
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
Copyright (c) 2012 - 2016, Victor Zverovich
|
||||||
|
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||||
|
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
452
third_party/spdlog/include/spdlog/fmt/bundled/chrono.h
vendored
Normal file
452
third_party/spdlog/include/spdlog/fmt/bundled/chrono.h
vendored
Normal file
@ -0,0 +1,452 @@
|
|||||||
|
// Formatting library for C++ - chrono support
|
||||||
|
//
|
||||||
|
// Copyright (c) 2012 - present, Victor Zverovich
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
|
#ifndef FMT_CHRONO_H_
|
||||||
|
#define FMT_CHRONO_H_
|
||||||
|
|
||||||
|
#include "format.h"
|
||||||
|
#include "locale.h"
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <ctime>
|
||||||
|
#include <locale>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
namespace internal{
|
||||||
|
|
||||||
|
enum class numeric_system {
|
||||||
|
standard,
|
||||||
|
// Alternative numeric system, e.g. 十二 instead of 12 in ja_JP locale.
|
||||||
|
alternative
|
||||||
|
};
|
||||||
|
|
||||||
|
// Parses a put_time-like format string and invokes handler actions.
|
||||||
|
template <typename Char, typename Handler>
|
||||||
|
FMT_CONSTEXPR const Char *parse_chrono_format(
|
||||||
|
const Char *begin, const Char *end, Handler &&handler) {
|
||||||
|
auto ptr = begin;
|
||||||
|
while (ptr != end) {
|
||||||
|
auto c = *ptr;
|
||||||
|
if (c == '}') break;
|
||||||
|
if (c != '%') {
|
||||||
|
++ptr;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (begin != ptr)
|
||||||
|
handler.on_text(begin, ptr);
|
||||||
|
++ptr; // consume '%'
|
||||||
|
if (ptr == end)
|
||||||
|
throw format_error("invalid format");
|
||||||
|
c = *ptr++;
|
||||||
|
switch (c) {
|
||||||
|
case '%':
|
||||||
|
handler.on_text(ptr - 1, ptr);
|
||||||
|
break;
|
||||||
|
case 'n': {
|
||||||
|
const char newline[] = "\n";
|
||||||
|
handler.on_text(newline, newline + 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 't': {
|
||||||
|
const char tab[] = "\t";
|
||||||
|
handler.on_text(tab, tab + 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Day of the week:
|
||||||
|
case 'a':
|
||||||
|
handler.on_abbr_weekday();
|
||||||
|
break;
|
||||||
|
case 'A':
|
||||||
|
handler.on_full_weekday();
|
||||||
|
break;
|
||||||
|
case 'w':
|
||||||
|
handler.on_dec0_weekday(numeric_system::standard);
|
||||||
|
break;
|
||||||
|
case 'u':
|
||||||
|
handler.on_dec1_weekday(numeric_system::standard);
|
||||||
|
break;
|
||||||
|
// Month:
|
||||||
|
case 'b':
|
||||||
|
handler.on_abbr_month();
|
||||||
|
break;
|
||||||
|
case 'B':
|
||||||
|
handler.on_full_month();
|
||||||
|
break;
|
||||||
|
// Hour, minute, second:
|
||||||
|
case 'H':
|
||||||
|
handler.on_24_hour(numeric_system::standard);
|
||||||
|
break;
|
||||||
|
case 'I':
|
||||||
|
handler.on_12_hour(numeric_system::standard);
|
||||||
|
break;
|
||||||
|
case 'M':
|
||||||
|
handler.on_minute(numeric_system::standard);
|
||||||
|
break;
|
||||||
|
case 'S':
|
||||||
|
handler.on_second(numeric_system::standard);
|
||||||
|
break;
|
||||||
|
// Other:
|
||||||
|
case 'c':
|
||||||
|
handler.on_datetime(numeric_system::standard);
|
||||||
|
break;
|
||||||
|
case 'x':
|
||||||
|
handler.on_loc_date(numeric_system::standard);
|
||||||
|
break;
|
||||||
|
case 'X':
|
||||||
|
handler.on_loc_time(numeric_system::standard);
|
||||||
|
break;
|
||||||
|
case 'D':
|
||||||
|
handler.on_us_date();
|
||||||
|
break;
|
||||||
|
case 'F':
|
||||||
|
handler.on_iso_date();
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
handler.on_12_hour_time();
|
||||||
|
break;
|
||||||
|
case 'R':
|
||||||
|
handler.on_24_hour_time();
|
||||||
|
break;
|
||||||
|
case 'T':
|
||||||
|
handler.on_iso_time();
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
handler.on_am_pm();
|
||||||
|
break;
|
||||||
|
case 'z':
|
||||||
|
handler.on_utc_offset();
|
||||||
|
break;
|
||||||
|
case 'Z':
|
||||||
|
handler.on_tz_name();
|
||||||
|
break;
|
||||||
|
// Alternative representation:
|
||||||
|
case 'E': {
|
||||||
|
if (ptr == end)
|
||||||
|
throw format_error("invalid format");
|
||||||
|
c = *ptr++;
|
||||||
|
switch (c) {
|
||||||
|
case 'c':
|
||||||
|
handler.on_datetime(numeric_system::alternative);
|
||||||
|
break;
|
||||||
|
case 'x':
|
||||||
|
handler.on_loc_date(numeric_system::alternative);
|
||||||
|
break;
|
||||||
|
case 'X':
|
||||||
|
handler.on_loc_time(numeric_system::alternative);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw format_error("invalid format");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'O':
|
||||||
|
if (ptr == end)
|
||||||
|
throw format_error("invalid format");
|
||||||
|
c = *ptr++;
|
||||||
|
switch (c) {
|
||||||
|
case 'w':
|
||||||
|
handler.on_dec0_weekday(numeric_system::alternative);
|
||||||
|
break;
|
||||||
|
case 'u':
|
||||||
|
handler.on_dec1_weekday(numeric_system::alternative);
|
||||||
|
break;
|
||||||
|
case 'H':
|
||||||
|
handler.on_24_hour(numeric_system::alternative);
|
||||||
|
break;
|
||||||
|
case 'I':
|
||||||
|
handler.on_12_hour(numeric_system::alternative);
|
||||||
|
break;
|
||||||
|
case 'M':
|
||||||
|
handler.on_minute(numeric_system::alternative);
|
||||||
|
break;
|
||||||
|
case 'S':
|
||||||
|
handler.on_second(numeric_system::alternative);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw format_error("invalid format");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw format_error("invalid format");
|
||||||
|
}
|
||||||
|
begin = ptr;
|
||||||
|
}
|
||||||
|
if (begin != ptr)
|
||||||
|
handler.on_text(begin, ptr);
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct chrono_format_checker {
|
||||||
|
void report_no_date() { throw format_error("no date"); }
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
void on_text(const Char *, const Char *) {}
|
||||||
|
void on_abbr_weekday() { report_no_date(); }
|
||||||
|
void on_full_weekday() { report_no_date(); }
|
||||||
|
void on_dec0_weekday(numeric_system) { report_no_date(); }
|
||||||
|
void on_dec1_weekday(numeric_system) { report_no_date(); }
|
||||||
|
void on_abbr_month() { report_no_date(); }
|
||||||
|
void on_full_month() { report_no_date(); }
|
||||||
|
void on_24_hour(numeric_system) {}
|
||||||
|
void on_12_hour(numeric_system) {}
|
||||||
|
void on_minute(numeric_system) {}
|
||||||
|
void on_second(numeric_system) {}
|
||||||
|
void on_datetime(numeric_system) { report_no_date(); }
|
||||||
|
void on_loc_date(numeric_system) { report_no_date(); }
|
||||||
|
void on_loc_time(numeric_system) { report_no_date(); }
|
||||||
|
void on_us_date() { report_no_date(); }
|
||||||
|
void on_iso_date() { report_no_date(); }
|
||||||
|
void on_12_hour_time() {}
|
||||||
|
void on_24_hour_time() {}
|
||||||
|
void on_iso_time() {}
|
||||||
|
void on_am_pm() {}
|
||||||
|
void on_utc_offset() { report_no_date(); }
|
||||||
|
void on_tz_name() { report_no_date(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Int>
|
||||||
|
inline int to_int(Int value) {
|
||||||
|
FMT_ASSERT(value >= (std::numeric_limits<int>::min)() &&
|
||||||
|
value <= (std::numeric_limits<int>::max)(), "invalid value");
|
||||||
|
return static_cast<int>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FormatContext, typename OutputIt>
|
||||||
|
struct chrono_formatter {
|
||||||
|
FormatContext &context;
|
||||||
|
OutputIt out;
|
||||||
|
std::chrono::seconds s;
|
||||||
|
std::chrono::milliseconds ms;
|
||||||
|
|
||||||
|
typedef typename FormatContext::char_type char_type;
|
||||||
|
|
||||||
|
explicit chrono_formatter(FormatContext &ctx, OutputIt o)
|
||||||
|
: context(ctx), out(o) {}
|
||||||
|
|
||||||
|
int hour() const { return to_int((s.count() / 3600) % 24); }
|
||||||
|
|
||||||
|
int hour12() const {
|
||||||
|
auto hour = to_int((s.count() / 3600) % 12);
|
||||||
|
return hour > 0 ? hour : 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
int minute() const { return to_int((s.count() / 60) % 60); }
|
||||||
|
int second() const { return to_int(s.count() % 60); }
|
||||||
|
|
||||||
|
std::tm time() const {
|
||||||
|
auto time = std::tm();
|
||||||
|
time.tm_hour = hour();
|
||||||
|
time.tm_min = minute();
|
||||||
|
time.tm_sec = second();
|
||||||
|
return time;
|
||||||
|
}
|
||||||
|
|
||||||
|
void write(int value, int width) {
|
||||||
|
typedef typename int_traits<int>::main_type main_type;
|
||||||
|
main_type n = to_unsigned(value);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
void format_localized(const tm &time, const char *format) {
|
||||||
|
auto locale = context.locale().template get<std::locale>();
|
||||||
|
auto &facet = std::use_facet<std::time_put<char_type>>(locale);
|
||||||
|
std::basic_ostringstream<char_type> os;
|
||||||
|
os.imbue(locale);
|
||||||
|
facet.put(os, os, ' ', &time, format, format + std::strlen(format));
|
||||||
|
auto str = os.str();
|
||||||
|
std::copy(str.begin(), str.end(), out);
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_text(const char_type *begin, const char_type *end) {
|
||||||
|
std::copy(begin, end, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
// These are not implemented because durations don't have date information.
|
||||||
|
void on_abbr_weekday() {}
|
||||||
|
void on_full_weekday() {}
|
||||||
|
void on_dec0_weekday(numeric_system) {}
|
||||||
|
void on_dec1_weekday(numeric_system) {}
|
||||||
|
void on_abbr_month() {}
|
||||||
|
void on_full_month() {}
|
||||||
|
void on_datetime(numeric_system) {}
|
||||||
|
void on_loc_date(numeric_system) {}
|
||||||
|
void on_loc_time(numeric_system) {}
|
||||||
|
void on_us_date() {}
|
||||||
|
void on_iso_date() {}
|
||||||
|
void on_utc_offset() {}
|
||||||
|
void on_tz_name() {}
|
||||||
|
|
||||||
|
void on_24_hour(numeric_system ns) {
|
||||||
|
if (ns == numeric_system::standard)
|
||||||
|
return write(hour(), 2);
|
||||||
|
auto time = tm();
|
||||||
|
time.tm_hour = hour();
|
||||||
|
format_localized(time, "%OH");
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_12_hour(numeric_system ns) {
|
||||||
|
if (ns == numeric_system::standard)
|
||||||
|
return write(hour12(), 2);
|
||||||
|
auto time = tm();
|
||||||
|
time.tm_hour = hour();
|
||||||
|
format_localized(time, "%OI");
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_minute(numeric_system ns) {
|
||||||
|
if (ns == numeric_system::standard)
|
||||||
|
return write(minute(), 2);
|
||||||
|
auto time = tm();
|
||||||
|
time.tm_min = minute();
|
||||||
|
format_localized(time, "%OM");
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_second(numeric_system ns) {
|
||||||
|
if (ns == numeric_system::standard) {
|
||||||
|
write(second(), 2);
|
||||||
|
if (ms != std::chrono::milliseconds(0)) {
|
||||||
|
*out++ = '.';
|
||||||
|
write(to_int(ms.count()), 3);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto time = tm();
|
||||||
|
time.tm_sec = second();
|
||||||
|
format_localized(time, "%OS");
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_12_hour_time() { format_localized(time(), "%r"); }
|
||||||
|
|
||||||
|
void on_24_hour_time() {
|
||||||
|
write(hour(), 2);
|
||||||
|
*out++ = ':';
|
||||||
|
write(minute(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_iso_time() {
|
||||||
|
on_24_hour_time();
|
||||||
|
*out++ = ':';
|
||||||
|
write(second(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_am_pm() { format_localized(time(), "%p"); }
|
||||||
|
};
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
template <typename Period> FMT_CONSTEXPR const char *get_units() {
|
||||||
|
return FMT_NULL;
|
||||||
|
}
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::atto>() { return "as"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::femto>() { return "fs"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::pico>() { return "ps"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::nano>() { return "ns"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::micro>() { return "µs"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::milli>() { return "ms"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::centi>() { return "cs"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::deci>() { return "ds"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::ratio<1>>() { return "s"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::deca>() { return "das"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::hecto>() { return "hs"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::kilo>() { return "ks"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::mega>() { return "Ms"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::giga>() { return "Gs"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::tera>() { return "Ts"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::peta>() { return "Ps"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::exa>() { return "Es"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::ratio<60>>() {
|
||||||
|
return "m";
|
||||||
|
}
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::ratio<3600>>() {
|
||||||
|
return "h";
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Rep, typename Period, typename Char>
|
||||||
|
struct formatter<std::chrono::duration<Rep, Period>, Char> {
|
||||||
|
private:
|
||||||
|
align_spec spec;
|
||||||
|
internal::arg_ref<Char> width_ref;
|
||||||
|
mutable basic_string_view<Char> format_str;
|
||||||
|
typedef std::chrono::duration<Rep, Period> duration;
|
||||||
|
|
||||||
|
struct spec_handler {
|
||||||
|
formatter &f;
|
||||||
|
basic_parse_context<Char> &context;
|
||||||
|
|
||||||
|
typedef internal::arg_ref<Char> arg_ref_type;
|
||||||
|
|
||||||
|
template <typename Id>
|
||||||
|
FMT_CONSTEXPR arg_ref_type make_arg_ref(Id arg_id) {
|
||||||
|
context.check_arg_id(arg_id);
|
||||||
|
return arg_ref_type(arg_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR arg_ref_type make_arg_ref(internal::auto_id) {
|
||||||
|
return arg_ref_type(context.next_arg_id());
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_error(const char *msg) { throw format_error(msg); }
|
||||||
|
void on_fill(Char fill) { f.spec.fill_ = fill; }
|
||||||
|
void on_align(alignment align) { f.spec.align_ = align; }
|
||||||
|
void on_width(unsigned width) { f.spec.width_ = width; }
|
||||||
|
|
||||||
|
template <typename Id>
|
||||||
|
void on_dynamic_width(Id arg_id) {
|
||||||
|
f.width_ref = make_arg_ref(arg_id);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
formatter() : spec() {}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR auto parse(basic_parse_context<Char> &ctx)
|
||||||
|
-> decltype(ctx.begin()) {
|
||||||
|
auto begin = ctx.begin(), end = ctx.end();
|
||||||
|
if (begin == end) return begin;
|
||||||
|
spec_handler handler{*this, ctx};
|
||||||
|
begin = internal::parse_align(begin, end, handler);
|
||||||
|
if (begin == end) return begin;
|
||||||
|
begin = internal::parse_width(begin, end, handler);
|
||||||
|
end = parse_chrono_format(begin, end, internal::chrono_format_checker());
|
||||||
|
format_str = basic_string_view<Char>(&*begin, internal::to_unsigned(end - begin));
|
||||||
|
return end;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(const duration &d, FormatContext &ctx)
|
||||||
|
-> decltype(ctx.out()) {
|
||||||
|
auto begin = format_str.begin(), end = format_str.end();
|
||||||
|
memory_buffer buf;
|
||||||
|
typedef output_range<decltype(ctx.out()), Char> range;
|
||||||
|
basic_writer<range> w(range(ctx.out()));
|
||||||
|
if (begin == end || *begin == '}') {
|
||||||
|
if (const char *unit = get_units<Period>())
|
||||||
|
format_to(buf, "{}{}", d.count(), unit);
|
||||||
|
else if (Period::den == 1)
|
||||||
|
format_to(buf, "{}[{}]s", d.count(), Period::num);
|
||||||
|
else
|
||||||
|
format_to(buf, "{}[{}/{}]s", d.count(), Period::num, Period::den);
|
||||||
|
internal::handle_dynamic_spec<internal::width_checker>(
|
||||||
|
spec.width_, width_ref, ctx);
|
||||||
|
} else {
|
||||||
|
auto out = std::back_inserter(buf);
|
||||||
|
internal::chrono_formatter<FormatContext, decltype(out)> f(ctx, out);
|
||||||
|
f.s = std::chrono::duration_cast<std::chrono::seconds>(d);
|
||||||
|
f.ms = std::chrono::duration_cast<std::chrono::milliseconds>(d - f.s);
|
||||||
|
parse_chrono_format(begin, end, f);
|
||||||
|
}
|
||||||
|
w.write(buf.data(), buf.size(), spec);
|
||||||
|
return w.out();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // FMT_CHRONO_H_
|
577
third_party/spdlog/include/spdlog/fmt/bundled/color.h
vendored
Normal file
577
third_party/spdlog/include/spdlog/fmt/bundled/color.h
vendored
Normal file
@ -0,0 +1,577 @@
|
|||||||
|
// Formatting library for C++ - color support
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018 - present, Victor Zverovich and fmt contributors
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
|
#ifndef FMT_COLOR_H_
|
||||||
|
#define FMT_COLOR_H_
|
||||||
|
|
||||||
|
#include "format.h"
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
#ifdef FMT_DEPRECATED_COLORS
|
||||||
|
|
||||||
|
// color and (v)print_colored are deprecated.
|
||||||
|
enum color { black, red, green, yellow, blue, magenta, cyan, white };
|
||||||
|
FMT_API void vprint_colored(color c, string_view format, format_args args);
|
||||||
|
FMT_API void vprint_colored(color c, wstring_view format, wformat_args args);
|
||||||
|
template <typename... Args>
|
||||||
|
inline void print_colored(color c, string_view format_str,
|
||||||
|
const Args & ... args) {
|
||||||
|
vprint_colored(c, format_str, make_format_args(args...));
|
||||||
|
}
|
||||||
|
template <typename... Args>
|
||||||
|
inline void print_colored(color c, wstring_view format_str,
|
||||||
|
const Args & ... args) {
|
||||||
|
vprint_colored(c, format_str, make_format_args<wformat_context>(args...));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void vprint_colored(color c, string_view format, format_args args) {
|
||||||
|
char escape[] = "\x1b[30m";
|
||||||
|
escape[3] = static_cast<char>('0' + c);
|
||||||
|
std::fputs(escape, stdout);
|
||||||
|
vprint(format, args);
|
||||||
|
std::fputs(internal::data::RESET_COLOR, stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void vprint_colored(color c, wstring_view format, wformat_args args) {
|
||||||
|
wchar_t escape[] = L"\x1b[30m";
|
||||||
|
escape[3] = static_cast<wchar_t>('0' + c);
|
||||||
|
std::fputws(escape, stdout);
|
||||||
|
vprint(format, args);
|
||||||
|
std::fputws(internal::data::WRESET_COLOR, stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
enum class color : uint32_t {
|
||||||
|
alice_blue = 0xF0F8FF, // rgb(240,248,255)
|
||||||
|
antique_white = 0xFAEBD7, // rgb(250,235,215)
|
||||||
|
aqua = 0x00FFFF, // rgb(0,255,255)
|
||||||
|
aquamarine = 0x7FFFD4, // rgb(127,255,212)
|
||||||
|
azure = 0xF0FFFF, // rgb(240,255,255)
|
||||||
|
beige = 0xF5F5DC, // rgb(245,245,220)
|
||||||
|
bisque = 0xFFE4C4, // rgb(255,228,196)
|
||||||
|
black = 0x000000, // rgb(0,0,0)
|
||||||
|
blanched_almond = 0xFFEBCD, // rgb(255,235,205)
|
||||||
|
blue = 0x0000FF, // rgb(0,0,255)
|
||||||
|
blue_violet = 0x8A2BE2, // rgb(138,43,226)
|
||||||
|
brown = 0xA52A2A, // rgb(165,42,42)
|
||||||
|
burly_wood = 0xDEB887, // rgb(222,184,135)
|
||||||
|
cadet_blue = 0x5F9EA0, // rgb(95,158,160)
|
||||||
|
chartreuse = 0x7FFF00, // rgb(127,255,0)
|
||||||
|
chocolate = 0xD2691E, // rgb(210,105,30)
|
||||||
|
coral = 0xFF7F50, // rgb(255,127,80)
|
||||||
|
cornflower_blue = 0x6495ED, // rgb(100,149,237)
|
||||||
|
cornsilk = 0xFFF8DC, // rgb(255,248,220)
|
||||||
|
crimson = 0xDC143C, // rgb(220,20,60)
|
||||||
|
cyan = 0x00FFFF, // rgb(0,255,255)
|
||||||
|
dark_blue = 0x00008B, // rgb(0,0,139)
|
||||||
|
dark_cyan = 0x008B8B, // rgb(0,139,139)
|
||||||
|
dark_golden_rod = 0xB8860B, // rgb(184,134,11)
|
||||||
|
dark_gray = 0xA9A9A9, // rgb(169,169,169)
|
||||||
|
dark_green = 0x006400, // rgb(0,100,0)
|
||||||
|
dark_khaki = 0xBDB76B, // rgb(189,183,107)
|
||||||
|
dark_magenta = 0x8B008B, // rgb(139,0,139)
|
||||||
|
dark_olive_green = 0x556B2F, // rgb(85,107,47)
|
||||||
|
dark_orange = 0xFF8C00, // rgb(255,140,0)
|
||||||
|
dark_orchid = 0x9932CC, // rgb(153,50,204)
|
||||||
|
dark_red = 0x8B0000, // rgb(139,0,0)
|
||||||
|
dark_salmon = 0xE9967A, // rgb(233,150,122)
|
||||||
|
dark_sea_green = 0x8FBC8F, // rgb(143,188,143)
|
||||||
|
dark_slate_blue = 0x483D8B, // rgb(72,61,139)
|
||||||
|
dark_slate_gray = 0x2F4F4F, // rgb(47,79,79)
|
||||||
|
dark_turquoise = 0x00CED1, // rgb(0,206,209)
|
||||||
|
dark_violet = 0x9400D3, // rgb(148,0,211)
|
||||||
|
deep_pink = 0xFF1493, // rgb(255,20,147)
|
||||||
|
deep_sky_blue = 0x00BFFF, // rgb(0,191,255)
|
||||||
|
dim_gray = 0x696969, // rgb(105,105,105)
|
||||||
|
dodger_blue = 0x1E90FF, // rgb(30,144,255)
|
||||||
|
fire_brick = 0xB22222, // rgb(178,34,34)
|
||||||
|
floral_white = 0xFFFAF0, // rgb(255,250,240)
|
||||||
|
forest_green = 0x228B22, // rgb(34,139,34)
|
||||||
|
fuchsia = 0xFF00FF, // rgb(255,0,255)
|
||||||
|
gainsboro = 0xDCDCDC, // rgb(220,220,220)
|
||||||
|
ghost_white = 0xF8F8FF, // rgb(248,248,255)
|
||||||
|
gold = 0xFFD700, // rgb(255,215,0)
|
||||||
|
golden_rod = 0xDAA520, // rgb(218,165,32)
|
||||||
|
gray = 0x808080, // rgb(128,128,128)
|
||||||
|
green = 0x008000, // rgb(0,128,0)
|
||||||
|
green_yellow = 0xADFF2F, // rgb(173,255,47)
|
||||||
|
honey_dew = 0xF0FFF0, // rgb(240,255,240)
|
||||||
|
hot_pink = 0xFF69B4, // rgb(255,105,180)
|
||||||
|
indian_red = 0xCD5C5C, // rgb(205,92,92)
|
||||||
|
indigo = 0x4B0082, // rgb(75,0,130)
|
||||||
|
ivory = 0xFFFFF0, // rgb(255,255,240)
|
||||||
|
khaki = 0xF0E68C, // rgb(240,230,140)
|
||||||
|
lavender = 0xE6E6FA, // rgb(230,230,250)
|
||||||
|
lavender_blush = 0xFFF0F5, // rgb(255,240,245)
|
||||||
|
lawn_green = 0x7CFC00, // rgb(124,252,0)
|
||||||
|
lemon_chiffon = 0xFFFACD, // rgb(255,250,205)
|
||||||
|
light_blue = 0xADD8E6, // rgb(173,216,230)
|
||||||
|
light_coral = 0xF08080, // rgb(240,128,128)
|
||||||
|
light_cyan = 0xE0FFFF, // rgb(224,255,255)
|
||||||
|
light_golden_rod_yellow = 0xFAFAD2, // rgb(250,250,210)
|
||||||
|
light_gray = 0xD3D3D3, // rgb(211,211,211)
|
||||||
|
light_green = 0x90EE90, // rgb(144,238,144)
|
||||||
|
light_pink = 0xFFB6C1, // rgb(255,182,193)
|
||||||
|
light_salmon = 0xFFA07A, // rgb(255,160,122)
|
||||||
|
light_sea_green = 0x20B2AA, // rgb(32,178,170)
|
||||||
|
light_sky_blue = 0x87CEFA, // rgb(135,206,250)
|
||||||
|
light_slate_gray = 0x778899, // rgb(119,136,153)
|
||||||
|
light_steel_blue = 0xB0C4DE, // rgb(176,196,222)
|
||||||
|
light_yellow = 0xFFFFE0, // rgb(255,255,224)
|
||||||
|
lime = 0x00FF00, // rgb(0,255,0)
|
||||||
|
lime_green = 0x32CD32, // rgb(50,205,50)
|
||||||
|
linen = 0xFAF0E6, // rgb(250,240,230)
|
||||||
|
magenta = 0xFF00FF, // rgb(255,0,255)
|
||||||
|
maroon = 0x800000, // rgb(128,0,0)
|
||||||
|
medium_aquamarine = 0x66CDAA, // rgb(102,205,170)
|
||||||
|
medium_blue = 0x0000CD, // rgb(0,0,205)
|
||||||
|
medium_orchid = 0xBA55D3, // rgb(186,85,211)
|
||||||
|
medium_purple = 0x9370DB, // rgb(147,112,219)
|
||||||
|
medium_sea_green = 0x3CB371, // rgb(60,179,113)
|
||||||
|
medium_slate_blue = 0x7B68EE, // rgb(123,104,238)
|
||||||
|
medium_spring_green = 0x00FA9A, // rgb(0,250,154)
|
||||||
|
medium_turquoise = 0x48D1CC, // rgb(72,209,204)
|
||||||
|
medium_violet_red = 0xC71585, // rgb(199,21,133)
|
||||||
|
midnight_blue = 0x191970, // rgb(25,25,112)
|
||||||
|
mint_cream = 0xF5FFFA, // rgb(245,255,250)
|
||||||
|
misty_rose = 0xFFE4E1, // rgb(255,228,225)
|
||||||
|
moccasin = 0xFFE4B5, // rgb(255,228,181)
|
||||||
|
navajo_white = 0xFFDEAD, // rgb(255,222,173)
|
||||||
|
navy = 0x000080, // rgb(0,0,128)
|
||||||
|
old_lace = 0xFDF5E6, // rgb(253,245,230)
|
||||||
|
olive = 0x808000, // rgb(128,128,0)
|
||||||
|
olive_drab = 0x6B8E23, // rgb(107,142,35)
|
||||||
|
orange = 0xFFA500, // rgb(255,165,0)
|
||||||
|
orange_red = 0xFF4500, // rgb(255,69,0)
|
||||||
|
orchid = 0xDA70D6, // rgb(218,112,214)
|
||||||
|
pale_golden_rod = 0xEEE8AA, // rgb(238,232,170)
|
||||||
|
pale_green = 0x98FB98, // rgb(152,251,152)
|
||||||
|
pale_turquoise = 0xAFEEEE, // rgb(175,238,238)
|
||||||
|
pale_violet_red = 0xDB7093, // rgb(219,112,147)
|
||||||
|
papaya_whip = 0xFFEFD5, // rgb(255,239,213)
|
||||||
|
peach_puff = 0xFFDAB9, // rgb(255,218,185)
|
||||||
|
peru = 0xCD853F, // rgb(205,133,63)
|
||||||
|
pink = 0xFFC0CB, // rgb(255,192,203)
|
||||||
|
plum = 0xDDA0DD, // rgb(221,160,221)
|
||||||
|
powder_blue = 0xB0E0E6, // rgb(176,224,230)
|
||||||
|
purple = 0x800080, // rgb(128,0,128)
|
||||||
|
rebecca_purple = 0x663399, // rgb(102,51,153)
|
||||||
|
red = 0xFF0000, // rgb(255,0,0)
|
||||||
|
rosy_brown = 0xBC8F8F, // rgb(188,143,143)
|
||||||
|
royal_blue = 0x4169E1, // rgb(65,105,225)
|
||||||
|
saddle_brown = 0x8B4513, // rgb(139,69,19)
|
||||||
|
salmon = 0xFA8072, // rgb(250,128,114)
|
||||||
|
sandy_brown = 0xF4A460, // rgb(244,164,96)
|
||||||
|
sea_green = 0x2E8B57, // rgb(46,139,87)
|
||||||
|
sea_shell = 0xFFF5EE, // rgb(255,245,238)
|
||||||
|
sienna = 0xA0522D, // rgb(160,82,45)
|
||||||
|
silver = 0xC0C0C0, // rgb(192,192,192)
|
||||||
|
sky_blue = 0x87CEEB, // rgb(135,206,235)
|
||||||
|
slate_blue = 0x6A5ACD, // rgb(106,90,205)
|
||||||
|
slate_gray = 0x708090, // rgb(112,128,144)
|
||||||
|
snow = 0xFFFAFA, // rgb(255,250,250)
|
||||||
|
spring_green = 0x00FF7F, // rgb(0,255,127)
|
||||||
|
steel_blue = 0x4682B4, // rgb(70,130,180)
|
||||||
|
tan = 0xD2B48C, // rgb(210,180,140)
|
||||||
|
teal = 0x008080, // rgb(0,128,128)
|
||||||
|
thistle = 0xD8BFD8, // rgb(216,191,216)
|
||||||
|
tomato = 0xFF6347, // rgb(255,99,71)
|
||||||
|
turquoise = 0x40E0D0, // rgb(64,224,208)
|
||||||
|
violet = 0xEE82EE, // rgb(238,130,238)
|
||||||
|
wheat = 0xF5DEB3, // rgb(245,222,179)
|
||||||
|
white = 0xFFFFFF, // rgb(255,255,255)
|
||||||
|
white_smoke = 0xF5F5F5, // rgb(245,245,245)
|
||||||
|
yellow = 0xFFFF00, // rgb(255,255,0)
|
||||||
|
yellow_green = 0x9ACD32 // rgb(154,205,50)
|
||||||
|
}; // enum class color
|
||||||
|
|
||||||
|
enum class terminal_color : uint8_t {
|
||||||
|
black = 30,
|
||||||
|
red,
|
||||||
|
green,
|
||||||
|
yellow,
|
||||||
|
blue,
|
||||||
|
magenta,
|
||||||
|
cyan,
|
||||||
|
white,
|
||||||
|
bright_black = 90,
|
||||||
|
bright_red,
|
||||||
|
bright_green,
|
||||||
|
bright_yellow,
|
||||||
|
bright_blue,
|
||||||
|
bright_magenta,
|
||||||
|
bright_cyan,
|
||||||
|
bright_white
|
||||||
|
}; // enum class terminal_color
|
||||||
|
|
||||||
|
enum class emphasis : uint8_t {
|
||||||
|
bold = 1,
|
||||||
|
italic = 1 << 1,
|
||||||
|
underline = 1 << 2,
|
||||||
|
strikethrough = 1 << 3
|
||||||
|
}; // enum class emphasis
|
||||||
|
|
||||||
|
// rgb is a struct for red, green and blue colors.
|
||||||
|
// We use rgb as name because some editors will show it as color direct in the
|
||||||
|
// editor.
|
||||||
|
struct rgb {
|
||||||
|
FMT_CONSTEXPR_DECL rgb() : r(0), g(0), b(0) {}
|
||||||
|
FMT_CONSTEXPR_DECL rgb(uint8_t r_, uint8_t g_, uint8_t b_)
|
||||||
|
: r(r_), g(g_), b(b_) {}
|
||||||
|
FMT_CONSTEXPR_DECL rgb(uint32_t hex)
|
||||||
|
: r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b((hex) & 0xFF) {}
|
||||||
|
FMT_CONSTEXPR_DECL rgb(color hex)
|
||||||
|
: r((uint32_t(hex) >> 16) & 0xFF), g((uint32_t(hex) >> 8) & 0xFF),
|
||||||
|
b(uint32_t(hex) & 0xFF) {}
|
||||||
|
uint8_t r;
|
||||||
|
uint8_t g;
|
||||||
|
uint8_t b;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
// color is a struct of either a rgb color or a terminal color.
|
||||||
|
struct color_type {
|
||||||
|
FMT_CONSTEXPR color_type() FMT_NOEXCEPT
|
||||||
|
: is_rgb(), value{} {}
|
||||||
|
FMT_CONSTEXPR color_type(color rgb_color) FMT_NOEXCEPT
|
||||||
|
: is_rgb(true), value{} {
|
||||||
|
value.rgb_color = static_cast<uint32_t>(rgb_color);
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR color_type(rgb rgb_color) FMT_NOEXCEPT
|
||||||
|
: is_rgb(true), value{} {
|
||||||
|
value.rgb_color = (static_cast<uint32_t>(rgb_color.r) << 16)
|
||||||
|
| (static_cast<uint32_t>(rgb_color.g) << 8) | rgb_color.b;
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR color_type(terminal_color term_color) FMT_NOEXCEPT
|
||||||
|
: is_rgb(), value{} {
|
||||||
|
value.term_color = static_cast<uint8_t>(term_color);
|
||||||
|
}
|
||||||
|
bool is_rgb;
|
||||||
|
union color_union {
|
||||||
|
uint8_t term_color;
|
||||||
|
uint32_t rgb_color;
|
||||||
|
} value;
|
||||||
|
};
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
// Experimental text formatting support.
|
||||||
|
class text_style {
|
||||||
|
public:
|
||||||
|
FMT_CONSTEXPR text_style(emphasis em = emphasis()) FMT_NOEXCEPT
|
||||||
|
: set_foreground_color(), set_background_color(), ems(em) {}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR text_style &operator|=(const text_style &rhs) {
|
||||||
|
if (!set_foreground_color) {
|
||||||
|
set_foreground_color = rhs.set_foreground_color;
|
||||||
|
foreground_color = rhs.foreground_color;
|
||||||
|
} else if (rhs.set_foreground_color) {
|
||||||
|
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
|
||||||
|
throw format_error("can't OR a terminal color");
|
||||||
|
foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!set_background_color) {
|
||||||
|
set_background_color = rhs.set_background_color;
|
||||||
|
background_color = rhs.background_color;
|
||||||
|
} else if (rhs.set_background_color) {
|
||||||
|
if (!background_color.is_rgb || !rhs.background_color.is_rgb)
|
||||||
|
throw format_error("can't OR a terminal color");
|
||||||
|
background_color.value.rgb_color |= rhs.background_color.value.rgb_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
ems = static_cast<emphasis>(static_cast<uint8_t>(ems) |
|
||||||
|
static_cast<uint8_t>(rhs.ems));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend FMT_CONSTEXPR
|
||||||
|
text_style operator|(text_style lhs, const text_style &rhs) {
|
||||||
|
return lhs |= rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR text_style &operator&=(const text_style &rhs) {
|
||||||
|
if (!set_foreground_color) {
|
||||||
|
set_foreground_color = rhs.set_foreground_color;
|
||||||
|
foreground_color = rhs.foreground_color;
|
||||||
|
} else if (rhs.set_foreground_color) {
|
||||||
|
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
|
||||||
|
throw format_error("can't AND a terminal color");
|
||||||
|
foreground_color.value.rgb_color &= rhs.foreground_color.value.rgb_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!set_background_color) {
|
||||||
|
set_background_color = rhs.set_background_color;
|
||||||
|
background_color = rhs.background_color;
|
||||||
|
} else if (rhs.set_background_color) {
|
||||||
|
if (!background_color.is_rgb || !rhs.background_color.is_rgb)
|
||||||
|
throw format_error("can't AND a terminal color");
|
||||||
|
background_color.value.rgb_color &= rhs.background_color.value.rgb_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
ems = static_cast<emphasis>(static_cast<uint8_t>(ems) &
|
||||||
|
static_cast<uint8_t>(rhs.ems));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend FMT_CONSTEXPR
|
||||||
|
text_style operator&(text_style lhs, const text_style &rhs) {
|
||||||
|
return lhs &= rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR bool has_foreground() const FMT_NOEXCEPT {
|
||||||
|
return set_foreground_color;
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR bool has_background() const FMT_NOEXCEPT {
|
||||||
|
return set_background_color;
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR bool has_emphasis() const FMT_NOEXCEPT {
|
||||||
|
return static_cast<uint8_t>(ems) != 0;
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR internal::color_type get_foreground() const FMT_NOEXCEPT {
|
||||||
|
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");
|
||||||
|
return background_color;
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR emphasis get_emphasis() const FMT_NOEXCEPT {
|
||||||
|
assert(has_emphasis() && "no emphasis specified for this style");
|
||||||
|
return ems;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
FMT_CONSTEXPR text_style(bool is_foreground,
|
||||||
|
internal::color_type text_color) FMT_NOEXCEPT
|
||||||
|
: set_foreground_color(),
|
||||||
|
set_background_color(),
|
||||||
|
ems() {
|
||||||
|
if (is_foreground) {
|
||||||
|
foreground_color = text_color;
|
||||||
|
set_foreground_color = true;
|
||||||
|
} else {
|
||||||
|
background_color = text_color;
|
||||||
|
set_background_color = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
friend FMT_CONSTEXPR_DECL text_style fg(internal::color_type foreground)
|
||||||
|
FMT_NOEXCEPT;
|
||||||
|
friend FMT_CONSTEXPR_DECL text_style bg(internal::color_type background)
|
||||||
|
FMT_NOEXCEPT;
|
||||||
|
|
||||||
|
internal::color_type foreground_color;
|
||||||
|
internal::color_type background_color;
|
||||||
|
bool set_foreground_color;
|
||||||
|
bool set_background_color;
|
||||||
|
emphasis ems;
|
||||||
|
};
|
||||||
|
|
||||||
|
FMT_CONSTEXPR text_style fg(internal::color_type foreground) FMT_NOEXCEPT {
|
||||||
|
return text_style(/*is_foreground=*/true, foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR text_style bg(internal::color_type background) FMT_NOEXCEPT {
|
||||||
|
return text_style(/*is_foreground=*/false, background);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR text_style operator|(emphasis lhs, emphasis rhs) FMT_NOEXCEPT {
|
||||||
|
return text_style(lhs) | rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
struct ansi_color_escape {
|
||||||
|
FMT_CONSTEXPR ansi_color_escape(internal::color_type text_color,
|
||||||
|
const char * esc) FMT_NOEXCEPT {
|
||||||
|
// If we have a terminal color, we need to output another escape code
|
||||||
|
// sequence.
|
||||||
|
if (!text_color.is_rgb) {
|
||||||
|
bool is_background = esc == internal::data::BACKGROUND_COLOR;
|
||||||
|
uint32_t value = text_color.value.term_color;
|
||||||
|
// Background ASCII codes are the same as the foreground ones but with
|
||||||
|
// 10 more.
|
||||||
|
if (is_background)
|
||||||
|
value += 10u;
|
||||||
|
|
||||||
|
std::size_t index = 0;
|
||||||
|
buffer[index++] = static_cast<Char>('\x1b');
|
||||||
|
buffer[index++] = static_cast<Char>('[');
|
||||||
|
|
||||||
|
if (value >= 100u) {
|
||||||
|
buffer[index++] = static_cast<Char>('1');
|
||||||
|
value %= 100u;
|
||||||
|
}
|
||||||
|
buffer[index++] = static_cast<Char>('0' + value / 10u);
|
||||||
|
buffer[index++] = static_cast<Char>('0' + value % 10u);
|
||||||
|
|
||||||
|
buffer[index++] = static_cast<Char>('m');
|
||||||
|
buffer[index++] = static_cast<Char>('\0');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 7; i++) {
|
||||||
|
buffer[i] = static_cast<Char>(esc[i]);
|
||||||
|
}
|
||||||
|
rgb color(text_color.value.rgb_color);
|
||||||
|
to_esc(color.r, buffer + 7, ';');
|
||||||
|
to_esc(color.g, buffer + 11, ';');
|
||||||
|
to_esc(color.b, buffer + 15, 'm');
|
||||||
|
buffer[19] = static_cast<Char>(0);
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR ansi_color_escape(emphasis em) FMT_NOEXCEPT {
|
||||||
|
uint8_t em_codes[4] = {};
|
||||||
|
uint8_t em_bits = static_cast<uint8_t>(em);
|
||||||
|
if (em_bits & static_cast<uint8_t>(emphasis::bold))
|
||||||
|
em_codes[0] = 1;
|
||||||
|
if (em_bits & static_cast<uint8_t>(emphasis::italic))
|
||||||
|
em_codes[1] = 3;
|
||||||
|
if (em_bits & static_cast<uint8_t>(emphasis::underline))
|
||||||
|
em_codes[2] = 4;
|
||||||
|
if (em_bits & static_cast<uint8_t>(emphasis::strikethrough))
|
||||||
|
em_codes[3] = 9;
|
||||||
|
|
||||||
|
std::size_t index = 0;
|
||||||
|
for (int i = 0; i < 4; ++i) {
|
||||||
|
if (!em_codes[i])
|
||||||
|
continue;
|
||||||
|
buffer[index++] = static_cast<Char>('\x1b');
|
||||||
|
buffer[index++] = static_cast<Char>('[');
|
||||||
|
buffer[index++] = static_cast<Char>('0' + em_codes[i]);
|
||||||
|
buffer[index++] = static_cast<Char>('m');
|
||||||
|
}
|
||||||
|
buffer[index++] = static_cast<Char>(0);
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR operator const Char *() const FMT_NOEXCEPT { return buffer; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Char buffer[7u + 3u * 4u + 1u];
|
||||||
|
|
||||||
|
static FMT_CONSTEXPR void to_esc(uint8_t c, Char *out,
|
||||||
|
char delimiter) FMT_NOEXCEPT {
|
||||||
|
out[0] = static_cast<Char>('0' + c / 100);
|
||||||
|
out[1] = static_cast<Char>('0' + c / 10 % 10);
|
||||||
|
out[2] = static_cast<Char>('0' + c % 10);
|
||||||
|
out[3] = static_cast<Char>(delimiter);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
FMT_CONSTEXPR ansi_color_escape<Char>
|
||||||
|
make_foreground_color(internal::color_type foreground) FMT_NOEXCEPT {
|
||||||
|
return ansi_color_escape<Char>(foreground, internal::data::FOREGROUND_COLOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
FMT_CONSTEXPR ansi_color_escape<Char>
|
||||||
|
make_background_color(internal::color_type background) FMT_NOEXCEPT {
|
||||||
|
return ansi_color_escape<Char>(background, internal::data::BACKGROUND_COLOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
FMT_CONSTEXPR ansi_color_escape<Char>
|
||||||
|
make_emphasis(emphasis em) FMT_NOEXCEPT {
|
||||||
|
return ansi_color_escape<Char>(em);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
inline void fputs(const Char *chars, FILE *stream) FMT_NOEXCEPT {
|
||||||
|
std::fputs(chars, stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline void fputs<wchar_t>(const wchar_t *chars, FILE *stream) FMT_NOEXCEPT {
|
||||||
|
std::fputws(chars, stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
inline void reset_color(FILE *stream) FMT_NOEXCEPT {
|
||||||
|
fputs(internal::data::RESET_COLOR, stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline void reset_color<wchar_t>(FILE *stream) FMT_NOEXCEPT {
|
||||||
|
fputs(internal::data::WRESET_COLOR, stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The following specialiazation disables using std::FILE as a character type,
|
||||||
|
// which is needed because or else
|
||||||
|
// fmt::print(stderr, fmt::emphasis::bold, "");
|
||||||
|
// would take stderr (a std::FILE *) as the format string.
|
||||||
|
template <>
|
||||||
|
struct is_string<std::FILE *> : std::false_type {};
|
||||||
|
template <>
|
||||||
|
struct is_string<const std::FILE *> : std::false_type {};
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
template <
|
||||||
|
typename S, typename Char = typename internal::char_t<S>::type>
|
||||||
|
void vprint(std::FILE *f, const text_style &ts, const S &format,
|
||||||
|
basic_format_args<typename buffer_context<Char>::type> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Formats a string and prints it to the specified file stream using ANSI
|
||||||
|
escape sequences to specify text formatting.
|
||||||
|
Example:
|
||||||
|
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
|
||||||
|
"Elapsed time: {0:.2f} seconds", 1.23);
|
||||||
|
*/
|
||||||
|
template <typename String, typename... Args>
|
||||||
|
typename std::enable_if<internal::is_string<String>::value>::type print(
|
||||||
|
std::FILE *f, const text_style &ts, const String &format_str,
|
||||||
|
const Args &... args) {
|
||||||
|
internal::check_format_string<Args...>(format_str);
|
||||||
|
typedef typename internal::char_t<String>::type char_t;
|
||||||
|
typedef typename buffer_context<char_t>::type context_t;
|
||||||
|
format_arg_store<context_t, Args...> as{args...};
|
||||||
|
vprint(f, ts, format_str, basic_format_args<context_t>(as));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Formats a string and prints it to stdout using ANSI escape sequences to
|
||||||
|
specify text formatting.
|
||||||
|
Example:
|
||||||
|
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
|
||||||
|
"Elapsed time: {0:.2f} seconds", 1.23);
|
||||||
|
*/
|
||||||
|
template <typename String, typename... Args>
|
||||||
|
typename std::enable_if<internal::is_string<String>::value>::type print(
|
||||||
|
const text_style &ts, const String &format_str,
|
||||||
|
const Args &... args) {
|
||||||
|
return print(stdout, ts, format_str, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // FMT_COLOR_H_
|
1502
third_party/spdlog/include/spdlog/fmt/bundled/core.h
vendored
Normal file
1502
third_party/spdlog/include/spdlog/fmt/bundled/core.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
972
third_party/spdlog/include/spdlog/fmt/bundled/format-inl.h
vendored
Normal file
972
third_party/spdlog/include/spdlog/fmt/bundled/format-inl.h
vendored
Normal file
@ -0,0 +1,972 @@
|
|||||||
|
// Formatting library for C++
|
||||||
|
//
|
||||||
|
// Copyright (c) 2012 - 2016, Victor Zverovich
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
|
#ifndef FMT_FORMAT_INL_H_
|
||||||
|
#define FMT_FORMAT_INL_H_
|
||||||
|
|
||||||
|
#include "format.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <cctype>
|
||||||
|
#include <cerrno>
|
||||||
|
#include <climits>
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstdarg>
|
||||||
|
#include <cstddef> // for std::ptrdiff_t
|
||||||
|
#include <cstring> // for std::memmove
|
||||||
|
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
|
||||||
|
# include <locale>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if FMT_USE_WINDOWS_H
|
||||||
|
# if !defined(FMT_HEADER_ONLY) && !defined(WIN32_LEAN_AND_MEAN)
|
||||||
|
# define WIN32_LEAN_AND_MEAN
|
||||||
|
# endif
|
||||||
|
# if defined(NOMINMAX) || defined(FMT_WIN_MINMAX)
|
||||||
|
# include <windows.h>
|
||||||
|
# else
|
||||||
|
# define NOMINMAX
|
||||||
|
# include <windows.h>
|
||||||
|
# undef NOMINMAX
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if FMT_EXCEPTIONS
|
||||||
|
# define FMT_TRY try
|
||||||
|
# define FMT_CATCH(x) catch (x)
|
||||||
|
#else
|
||||||
|
# define FMT_TRY if (true)
|
||||||
|
# define FMT_CATCH(x) if (false)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
# pragma warning(push)
|
||||||
|
# pragma warning(disable: 4127) // conditional expression is constant
|
||||||
|
# pragma warning(disable: 4702) // unreachable code
|
||||||
|
// Disable deprecation warning for strerror. The latter is not called but
|
||||||
|
// MSVC fails to detect it.
|
||||||
|
# pragma warning(disable: 4996)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Dummy implementations of strerror_r and strerror_s called if corresponding
|
||||||
|
// system functions are not available.
|
||||||
|
inline fmt::internal::null<> strerror_r(int, char *, ...) {
|
||||||
|
return fmt::internal::null<>();
|
||||||
|
}
|
||||||
|
inline fmt::internal::null<> strerror_s(char *, std::size_t, ...) {
|
||||||
|
return fmt::internal::null<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
#ifndef _MSC_VER
|
||||||
|
# define FMT_SNPRINTF snprintf
|
||||||
|
#else // _MSC_VER
|
||||||
|
inline int fmt_snprintf(char *buffer, size_t size, const char *format, ...) {
|
||||||
|
va_list args;
|
||||||
|
va_start(args, format);
|
||||||
|
int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args);
|
||||||
|
va_end(args);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
# define FMT_SNPRINTF fmt_snprintf
|
||||||
|
#endif // _MSC_VER
|
||||||
|
|
||||||
|
#if defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT)
|
||||||
|
# define FMT_SWPRINTF snwprintf
|
||||||
|
#else
|
||||||
|
# define FMT_SWPRINTF swprintf
|
||||||
|
#endif // defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT)
|
||||||
|
|
||||||
|
typedef void (*FormatFunc)(internal::buffer &, int, string_view);
|
||||||
|
|
||||||
|
// Portable thread-safe version of strerror.
|
||||||
|
// Sets buffer to point to a string describing the error code.
|
||||||
|
// This can be either a pointer to a string stored in buffer,
|
||||||
|
// or a pointer to some static immutable string.
|
||||||
|
// Returns one of the following values:
|
||||||
|
// 0 - success
|
||||||
|
// ERANGE - buffer is not large enough to store the error message
|
||||||
|
// other - failure
|
||||||
|
// Buffer should be at least of size 1.
|
||||||
|
int safe_strerror(
|
||||||
|
int error_code, char *&buffer, std::size_t buffer_size) FMT_NOEXCEPT {
|
||||||
|
FMT_ASSERT(buffer != FMT_NULL && buffer_size != 0, "invalid buffer");
|
||||||
|
|
||||||
|
class dispatcher {
|
||||||
|
private:
|
||||||
|
int error_code_;
|
||||||
|
char *&buffer_;
|
||||||
|
std::size_t buffer_size_;
|
||||||
|
|
||||||
|
// A noop assignment operator to avoid bogus warnings.
|
||||||
|
void operator=(const dispatcher &) {}
|
||||||
|
|
||||||
|
// Handle the result of XSI-compliant version of strerror_r.
|
||||||
|
int handle(int result) {
|
||||||
|
// glibc versions before 2.13 return result in errno.
|
||||||
|
return result == -1 ? errno : result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle the result of GNU-specific version of strerror_r.
|
||||||
|
int handle(char *message) {
|
||||||
|
// If the buffer is full then the message is probably truncated.
|
||||||
|
if (message == buffer_ && strlen(buffer_) == buffer_size_ - 1)
|
||||||
|
return ERANGE;
|
||||||
|
buffer_ = message;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle the case when strerror_r is not available.
|
||||||
|
int handle(internal::null<>) {
|
||||||
|
return fallback(strerror_s(buffer_, buffer_size_, error_code_));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to strerror_s when strerror_r is not available.
|
||||||
|
int fallback(int result) {
|
||||||
|
// If the buffer is full then the message is probably truncated.
|
||||||
|
return result == 0 && strlen(buffer_) == buffer_size_ - 1 ?
|
||||||
|
ERANGE : result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !FMT_MSC_VER
|
||||||
|
// Fallback to strerror if strerror_r and strerror_s are not available.
|
||||||
|
int fallback(internal::null<>) {
|
||||||
|
errno = 0;
|
||||||
|
buffer_ = strerror(error_code_);
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public:
|
||||||
|
dispatcher(int err_code, char *&buf, std::size_t buf_size)
|
||||||
|
: error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {}
|
||||||
|
|
||||||
|
int run() {
|
||||||
|
return handle(strerror_r(error_code_, buffer_, buffer_size_));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return dispatcher(error_code, buffer, buffer_size).run();
|
||||||
|
}
|
||||||
|
|
||||||
|
void format_error_code(internal::buffer &out, int error_code,
|
||||||
|
string_view message) FMT_NOEXCEPT {
|
||||||
|
// Report error code making sure that the output fits into
|
||||||
|
// inline_buffer_size to avoid dynamic memory allocation and potential
|
||||||
|
// bad_alloc.
|
||||||
|
out.resize(0);
|
||||||
|
static const char SEP[] = ": ";
|
||||||
|
static const char ERROR_STR[] = "error ";
|
||||||
|
// Subtract 2 to account for terminating null characters in SEP and ERROR_STR.
|
||||||
|
std::size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2;
|
||||||
|
typedef internal::int_traits<int>::main_type main_type;
|
||||||
|
main_type abs_value = static_cast<main_type>(error_code);
|
||||||
|
if (internal::is_negative(error_code)) {
|
||||||
|
abs_value = 0 - abs_value;
|
||||||
|
++error_code_size;
|
||||||
|
}
|
||||||
|
error_code_size += internal::to_unsigned(internal::count_digits(abs_value));
|
||||||
|
writer w(out);
|
||||||
|
if (message.size() <= inline_buffer_size - error_code_size) {
|
||||||
|
w.write(message);
|
||||||
|
w.write(SEP);
|
||||||
|
}
|
||||||
|
w.write(ERROR_STR);
|
||||||
|
w.write(error_code);
|
||||||
|
assert(out.size() <= inline_buffer_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void report_error(FormatFunc func, int error_code,
|
||||||
|
string_view message) FMT_NOEXCEPT {
|
||||||
|
memory_buffer full_message;
|
||||||
|
func(full_message, error_code, message);
|
||||||
|
// Use Writer::data instead of Writer::c_str to avoid potential memory
|
||||||
|
// allocation.
|
||||||
|
std::fwrite(full_message.data(), full_message.size(), 1, stderr);
|
||||||
|
std::fputc('\n', stderr);
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
FMT_FUNC size_t internal::count_code_points(basic_string_view<char8_t> s) {
|
||||||
|
const char8_t *data = s.data();
|
||||||
|
size_t num_code_points = 0;
|
||||||
|
for (size_t i = 0, size = s.size(); i != size; ++i) {
|
||||||
|
if ((data[i] & 0xc0) != 0x80)
|
||||||
|
++num_code_points;
|
||||||
|
}
|
||||||
|
return num_code_points;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
template <typename Locale>
|
||||||
|
locale_ref::locale_ref(const Locale &loc) : locale_(&loc) {
|
||||||
|
static_assert(std::is_same<Locale, std::locale>::value, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Locale>
|
||||||
|
Locale locale_ref::get() const {
|
||||||
|
static_assert(std::is_same<Locale, std::locale>::value, "");
|
||||||
|
return locale_ ? *static_cast<const std::locale*>(locale_) : std::locale();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
FMT_FUNC Char thousands_sep_impl(locale_ref loc) {
|
||||||
|
return std::use_facet<std::numpunct<Char> >(
|
||||||
|
loc.get<std::locale>()).thousands_sep();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
template <typename Char>
|
||||||
|
FMT_FUNC Char internal::thousands_sep_impl(locale_ref) {
|
||||||
|
return FMT_STATIC_THOUSANDS_SEPARATOR;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
FMT_FUNC void system_error::init(
|
||||||
|
int err_code, string_view format_str, format_args args) {
|
||||||
|
error_code_ = err_code;
|
||||||
|
memory_buffer buffer;
|
||||||
|
format_system_error(buffer, err_code, vformat(format_str, args));
|
||||||
|
std::runtime_error &base = *this;
|
||||||
|
base = std::runtime_error(to_string(buffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
template <typename T>
|
||||||
|
int char_traits<char>::format_float(
|
||||||
|
char *buf, std::size_t size, const char *format, int precision, T value) {
|
||||||
|
return precision < 0 ?
|
||||||
|
FMT_SNPRINTF(buf, size, format, value) :
|
||||||
|
FMT_SNPRINTF(buf, size, format, precision, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
int char_traits<wchar_t>::format_float(
|
||||||
|
wchar_t *buf, std::size_t size, const wchar_t *format, int precision,
|
||||||
|
T value) {
|
||||||
|
return precision < 0 ?
|
||||||
|
FMT_SWPRINTF(buf, size, format, value) :
|
||||||
|
FMT_SWPRINTF(buf, size, format, precision, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
const char basic_data<T>::DIGITS[] =
|
||||||
|
"0001020304050607080910111213141516171819"
|
||||||
|
"2021222324252627282930313233343536373839"
|
||||||
|
"4041424344454647484950515253545556575859"
|
||||||
|
"6061626364656667686970717273747576777879"
|
||||||
|
"8081828384858687888990919293949596979899";
|
||||||
|
|
||||||
|
#define FMT_POWERS_OF_10(factor) \
|
||||||
|
factor * 10, \
|
||||||
|
factor * 100, \
|
||||||
|
factor * 1000, \
|
||||||
|
factor * 10000, \
|
||||||
|
factor * 100000, \
|
||||||
|
factor * 1000000, \
|
||||||
|
factor * 10000000, \
|
||||||
|
factor * 100000000, \
|
||||||
|
factor * 1000000000
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
const uint32_t basic_data<T>::POWERS_OF_10_32[] = {
|
||||||
|
1, FMT_POWERS_OF_10(1)
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
const uint32_t basic_data<T>::ZERO_OR_POWERS_OF_10_32[] = {
|
||||||
|
0, FMT_POWERS_OF_10(1)
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
const uint64_t basic_data<T>::ZERO_OR_POWERS_OF_10_64[] = {
|
||||||
|
0,
|
||||||
|
FMT_POWERS_OF_10(1),
|
||||||
|
FMT_POWERS_OF_10(1000000000ull),
|
||||||
|
10000000000000000000ull
|
||||||
|
};
|
||||||
|
|
||||||
|
// Normalized 64-bit significands of pow(10, k), for k = -348, -340, ..., 340.
|
||||||
|
// These are generated by support/compute-powers.py.
|
||||||
|
template <typename T>
|
||||||
|
const uint64_t basic_data<T>::POW10_SIGNIFICANDS[] = {
|
||||||
|
0xfa8fd5a0081c0288, 0xbaaee17fa23ebf76, 0x8b16fb203055ac76,
|
||||||
|
0xcf42894a5dce35ea, 0x9a6bb0aa55653b2d, 0xe61acf033d1a45df,
|
||||||
|
0xab70fe17c79ac6ca, 0xff77b1fcbebcdc4f, 0xbe5691ef416bd60c,
|
||||||
|
0x8dd01fad907ffc3c, 0xd3515c2831559a83, 0x9d71ac8fada6c9b5,
|
||||||
|
0xea9c227723ee8bcb, 0xaecc49914078536d, 0x823c12795db6ce57,
|
||||||
|
0xc21094364dfb5637, 0x9096ea6f3848984f, 0xd77485cb25823ac7,
|
||||||
|
0xa086cfcd97bf97f4, 0xef340a98172aace5, 0xb23867fb2a35b28e,
|
||||||
|
0x84c8d4dfd2c63f3b, 0xc5dd44271ad3cdba, 0x936b9fcebb25c996,
|
||||||
|
0xdbac6c247d62a584, 0xa3ab66580d5fdaf6, 0xf3e2f893dec3f126,
|
||||||
|
0xb5b5ada8aaff80b8, 0x87625f056c7c4a8b, 0xc9bcff6034c13053,
|
||||||
|
0x964e858c91ba2655, 0xdff9772470297ebd, 0xa6dfbd9fb8e5b88f,
|
||||||
|
0xf8a95fcf88747d94, 0xb94470938fa89bcf, 0x8a08f0f8bf0f156b,
|
||||||
|
0xcdb02555653131b6, 0x993fe2c6d07b7fac, 0xe45c10c42a2b3b06,
|
||||||
|
0xaa242499697392d3, 0xfd87b5f28300ca0e, 0xbce5086492111aeb,
|
||||||
|
0x8cbccc096f5088cc, 0xd1b71758e219652c, 0x9c40000000000000,
|
||||||
|
0xe8d4a51000000000, 0xad78ebc5ac620000, 0x813f3978f8940984,
|
||||||
|
0xc097ce7bc90715b3, 0x8f7e32ce7bea5c70, 0xd5d238a4abe98068,
|
||||||
|
0x9f4f2726179a2245, 0xed63a231d4c4fb27, 0xb0de65388cc8ada8,
|
||||||
|
0x83c7088e1aab65db, 0xc45d1df942711d9a, 0x924d692ca61be758,
|
||||||
|
0xda01ee641a708dea, 0xa26da3999aef774a, 0xf209787bb47d6b85,
|
||||||
|
0xb454e4a179dd1877, 0x865b86925b9bc5c2, 0xc83553c5c8965d3d,
|
||||||
|
0x952ab45cfa97a0b3, 0xde469fbd99a05fe3, 0xa59bc234db398c25,
|
||||||
|
0xf6c69a72a3989f5c, 0xb7dcbf5354e9bece, 0x88fcf317f22241e2,
|
||||||
|
0xcc20ce9bd35c78a5, 0x98165af37b2153df, 0xe2a0b5dc971f303a,
|
||||||
|
0xa8d9d1535ce3b396, 0xfb9b7cd9a4a7443c, 0xbb764c4ca7a44410,
|
||||||
|
0x8bab8eefb6409c1a, 0xd01fef10a657842c, 0x9b10a4e5e9913129,
|
||||||
|
0xe7109bfba19c0c9d, 0xac2820d9623bf429, 0x80444b5e7aa7cf85,
|
||||||
|
0xbf21e44003acdd2d, 0x8e679c2f5e44ff8f, 0xd433179d9c8cb841,
|
||||||
|
0x9e19db92b4e31ba9, 0xeb96bf6ebadf77d9, 0xaf87023b9bf0ee6b,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Binary exponents of pow(10, k), for k = -348, -340, ..., 340, corresponding
|
||||||
|
// to significands above.
|
||||||
|
template <typename T>
|
||||||
|
const int16_t basic_data<T>::POW10_EXPONENTS[] = {
|
||||||
|
-1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, -954,
|
||||||
|
-927, -901, -874, -847, -821, -794, -768, -741, -715, -688, -661,
|
||||||
|
-635, -608, -582, -555, -529, -502, -475, -449, -422, -396, -369,
|
||||||
|
-343, -316, -289, -263, -236, -210, -183, -157, -130, -103, -77,
|
||||||
|
-50, -24, 3, 30, 56, 83, 109, 136, 162, 189, 216,
|
||||||
|
242, 269, 295, 322, 348, 375, 402, 428, 455, 481, 508,
|
||||||
|
534, 561, 588, 614, 641, 667, 694, 720, 747, 774, 800,
|
||||||
|
827, 853, 880, 907, 933, 960, 986, 1013, 1039, 1066
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T> const char basic_data<T>::FOREGROUND_COLOR[] = "\x1b[38;2;";
|
||||||
|
template <typename T> const char basic_data<T>::BACKGROUND_COLOR[] = "\x1b[48;2;";
|
||||||
|
template <typename T> const char basic_data<T>::RESET_COLOR[] = "\x1b[0m";
|
||||||
|
template <typename T> const wchar_t basic_data<T>::WRESET_COLOR[] = L"\x1b[0m";
|
||||||
|
|
||||||
|
// A handmade floating-point number f * pow(2, e).
|
||||||
|
class fp {
|
||||||
|
private:
|
||||||
|
typedef uint64_t significand_type;
|
||||||
|
|
||||||
|
// All sizes are in bits.
|
||||||
|
static FMT_CONSTEXPR_DECL const int char_size =
|
||||||
|
std::numeric_limits<unsigned char>::digits;
|
||||||
|
// Subtract 1 to account for an implicit most significant bit in the
|
||||||
|
// normalized form.
|
||||||
|
static FMT_CONSTEXPR_DECL const int double_significand_size =
|
||||||
|
std::numeric_limits<double>::digits - 1;
|
||||||
|
static FMT_CONSTEXPR_DECL const uint64_t implicit_bit =
|
||||||
|
1ull << double_significand_size;
|
||||||
|
|
||||||
|
public:
|
||||||
|
significand_type f;
|
||||||
|
int e;
|
||||||
|
|
||||||
|
static FMT_CONSTEXPR_DECL const int significand_size =
|
||||||
|
sizeof(significand_type) * char_size;
|
||||||
|
|
||||||
|
fp(): f(0), e(0) {}
|
||||||
|
fp(uint64_t f_val, int e_val): f(f_val), e(e_val) {}
|
||||||
|
|
||||||
|
// Constructs fp from an IEEE754 double. It is a template to prevent compile
|
||||||
|
// errors on platforms where double is not IEEE754.
|
||||||
|
template <typename Double>
|
||||||
|
explicit fp(Double d) {
|
||||||
|
// Assume double is in the format [sign][exponent][significand].
|
||||||
|
typedef std::numeric_limits<Double> limits;
|
||||||
|
const int double_size = static_cast<int>(sizeof(Double) * char_size);
|
||||||
|
const int exponent_size =
|
||||||
|
double_size - double_significand_size - 1; // -1 for sign
|
||||||
|
const uint64_t significand_mask = implicit_bit - 1;
|
||||||
|
const uint64_t exponent_mask = (~0ull >> 1) & ~significand_mask;
|
||||||
|
const int exponent_bias = (1 << exponent_size) - limits::max_exponent - 1;
|
||||||
|
auto u = bit_cast<uint64_t>(d);
|
||||||
|
auto biased_e = (u & exponent_mask) >> double_significand_size;
|
||||||
|
f = u & significand_mask;
|
||||||
|
if (biased_e != 0)
|
||||||
|
f += implicit_bit;
|
||||||
|
else
|
||||||
|
biased_e = 1; // Subnormals use biased exponent 1 (min exponent).
|
||||||
|
e = static_cast<int>(biased_e - exponent_bias - double_significand_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalizes the value converted from double and multiplied by (1 << SHIFT).
|
||||||
|
template <int SHIFT = 0>
|
||||||
|
void normalize() {
|
||||||
|
// Handle subnormals.
|
||||||
|
auto shifted_implicit_bit = implicit_bit << SHIFT;
|
||||||
|
while ((f & shifted_implicit_bit) == 0) {
|
||||||
|
f <<= 1;
|
||||||
|
--e;
|
||||||
|
}
|
||||||
|
// Subtract 1 to account for hidden bit.
|
||||||
|
auto offset = significand_size - double_significand_size - SHIFT - 1;
|
||||||
|
f <<= offset;
|
||||||
|
e -= offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute lower and upper boundaries (m^- and m^+ in the Grisu paper), where
|
||||||
|
// a boundary is a value half way between the number and its predecessor
|
||||||
|
// (lower) or successor (upper). The upper boundary is normalized and lower
|
||||||
|
// has the same exponent but may be not normalized.
|
||||||
|
void compute_boundaries(fp &lower, fp &upper) const {
|
||||||
|
lower = f == implicit_bit ?
|
||||||
|
fp((f << 2) - 1, e - 2) : fp((f << 1) - 1, e - 1);
|
||||||
|
upper = fp((f << 1) + 1, e - 1);
|
||||||
|
upper.normalize<1>(); // 1 is to account for the exponent shift above.
|
||||||
|
lower.f <<= lower.e - upper.e;
|
||||||
|
lower.e = upper.e;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Returns an fp number representing x - y. Result may not be normalized.
|
||||||
|
inline fp operator-(fp x, fp y) {
|
||||||
|
FMT_ASSERT(x.f >= y.f && x.e == y.e, "invalid operands");
|
||||||
|
return fp(x.f - y.f, x.e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Computes an fp number r with r.f = x.f * y.f / pow(2, 64) rounded to nearest
|
||||||
|
// with half-up tie breaking, r.e = x.e + y.e + 64. Result may not be normalized.
|
||||||
|
FMT_API fp operator*(fp x, fp y);
|
||||||
|
|
||||||
|
// Returns cached power (of 10) c_k = c_k.f * pow(2, c_k.e) such that its
|
||||||
|
// (binary) exponent satisfies min_exponent <= c_k.e <= min_exponent + 3.
|
||||||
|
FMT_API fp get_cached_power(int min_exponent, int &pow10_exponent);
|
||||||
|
|
||||||
|
FMT_FUNC fp operator*(fp x, fp y) {
|
||||||
|
// Multiply 32-bit parts of significands.
|
||||||
|
uint64_t mask = (1ULL << 32) - 1;
|
||||||
|
uint64_t a = x.f >> 32, b = x.f & mask;
|
||||||
|
uint64_t c = y.f >> 32, d = y.f & mask;
|
||||||
|
uint64_t ac = a * c, bc = b * c, ad = a * d, bd = b * d;
|
||||||
|
// Compute mid 64-bit of result and round.
|
||||||
|
uint64_t mid = (bd >> 32) + (ad & mask) + (bc & mask) + (1U << 31);
|
||||||
|
return fp(ac + (ad >> 32) + (bc >> 32) + (mid >> 32), x.e + y.e + 64);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_FUNC fp get_cached_power(int min_exponent, int &pow10_exponent) {
|
||||||
|
const double one_over_log2_10 = 0.30102999566398114; // 1 / log2(10)
|
||||||
|
int index = static_cast<int>(std::ceil(
|
||||||
|
(min_exponent + fp::significand_size - 1) * one_over_log2_10));
|
||||||
|
// Decimal exponent of the first (smallest) cached power of 10.
|
||||||
|
const int first_dec_exp = -348;
|
||||||
|
// Difference between 2 consecutive decimal exponents in cached powers of 10.
|
||||||
|
const int dec_exp_step = 8;
|
||||||
|
index = (index - first_dec_exp - 1) / dec_exp_step + 1;
|
||||||
|
pow10_exponent = first_dec_exp + index * dec_exp_step;
|
||||||
|
return fp(data::POW10_SIGNIFICANDS[index], data::POW10_EXPONENTS[index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_FUNC bool grisu2_round(
|
||||||
|
char *buf, int &size, int max_digits, uint64_t delta,
|
||||||
|
uint64_t remainder, uint64_t exp, uint64_t diff, int &exp10) {
|
||||||
|
while (remainder < diff && delta - remainder >= exp &&
|
||||||
|
(remainder + exp < diff || diff - remainder > remainder + exp - diff)) {
|
||||||
|
--buf[size - 1];
|
||||||
|
remainder += exp;
|
||||||
|
}
|
||||||
|
if (size > max_digits) {
|
||||||
|
--size;
|
||||||
|
++exp10;
|
||||||
|
if (buf[size] >= '5')
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generates output using Grisu2 digit-gen algorithm.
|
||||||
|
FMT_FUNC bool grisu2_gen_digits(
|
||||||
|
char *buf, int &size, uint32_t hi, uint64_t lo, int &exp,
|
||||||
|
uint64_t delta, const fp &one, const fp &diff, int max_digits) {
|
||||||
|
// Generate digits for the most significant part (hi).
|
||||||
|
while (exp > 0) {
|
||||||
|
uint32_t digit = 0;
|
||||||
|
// This optimization by miloyip reduces the number of integer divisions by
|
||||||
|
// one per iteration.
|
||||||
|
switch (exp) {
|
||||||
|
case 10: digit = hi / 1000000000; hi %= 1000000000; break;
|
||||||
|
case 9: digit = hi / 100000000; hi %= 100000000; break;
|
||||||
|
case 8: digit = hi / 10000000; hi %= 10000000; break;
|
||||||
|
case 7: digit = hi / 1000000; hi %= 1000000; break;
|
||||||
|
case 6: digit = hi / 100000; hi %= 100000; break;
|
||||||
|
case 5: digit = hi / 10000; hi %= 10000; break;
|
||||||
|
case 4: digit = hi / 1000; hi %= 1000; break;
|
||||||
|
case 3: digit = hi / 100; hi %= 100; break;
|
||||||
|
case 2: digit = hi / 10; hi %= 10; break;
|
||||||
|
case 1: digit = hi; hi = 0; break;
|
||||||
|
default:
|
||||||
|
FMT_ASSERT(false, "invalid number of digits");
|
||||||
|
}
|
||||||
|
if (digit != 0 || size != 0)
|
||||||
|
buf[size++] = static_cast<char>('0' + digit);
|
||||||
|
--exp;
|
||||||
|
uint64_t remainder = (static_cast<uint64_t>(hi) << -one.e) + lo;
|
||||||
|
if (remainder <= delta || size > max_digits) {
|
||||||
|
return grisu2_round(
|
||||||
|
buf, size, max_digits, delta, remainder,
|
||||||
|
static_cast<uint64_t>(data::POWERS_OF_10_32[exp]) << -one.e,
|
||||||
|
diff.f, exp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Generate digits for the least significant part (lo).
|
||||||
|
for (;;) {
|
||||||
|
lo *= 10;
|
||||||
|
delta *= 10;
|
||||||
|
char digit = static_cast<char>(lo >> -one.e);
|
||||||
|
if (digit != 0 || size != 0)
|
||||||
|
buf[size++] = static_cast<char>('0' + digit);
|
||||||
|
lo &= one.f - 1;
|
||||||
|
--exp;
|
||||||
|
if (lo < delta || size > max_digits) {
|
||||||
|
return grisu2_round(buf, size, max_digits, delta, lo, one.f,
|
||||||
|
diff.f * data::POWERS_OF_10_32[-exp], exp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if FMT_CLANG_VERSION
|
||||||
|
# define FMT_FALLTHROUGH [[clang::fallthrough]];
|
||||||
|
#elif FMT_GCC_VERSION >= 700
|
||||||
|
# define FMT_FALLTHROUGH [[gnu::fallthrough]];
|
||||||
|
#else
|
||||||
|
# define FMT_FALLTHROUGH
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct gen_digits_params {
|
||||||
|
int num_digits;
|
||||||
|
bool fixed;
|
||||||
|
bool upper;
|
||||||
|
bool trailing_zeros;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct prettify_handler {
|
||||||
|
char *data;
|
||||||
|
ptrdiff_t size;
|
||||||
|
buffer &buf;
|
||||||
|
|
||||||
|
explicit prettify_handler(buffer &b, ptrdiff_t n)
|
||||||
|
: data(b.data()), size(n), buf(b) {}
|
||||||
|
~prettify_handler() {
|
||||||
|
assert(buf.size() >= to_unsigned(size));
|
||||||
|
buf.resize(to_unsigned(size));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
void insert(ptrdiff_t pos, ptrdiff_t n, F f) {
|
||||||
|
std::memmove(data + pos + n, data + pos, to_unsigned(size - pos));
|
||||||
|
f(data + pos);
|
||||||
|
size += n;
|
||||||
|
}
|
||||||
|
|
||||||
|
void insert(ptrdiff_t pos, char c) {
|
||||||
|
std::memmove(data + pos + 1, data + pos, to_unsigned(size - pos));
|
||||||
|
data[pos] = c;
|
||||||
|
++size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void append(ptrdiff_t n, char c) {
|
||||||
|
std::uninitialized_fill_n(data + size, n, c);
|
||||||
|
size += n;
|
||||||
|
}
|
||||||
|
|
||||||
|
void append(char c) { data[size++] = c; }
|
||||||
|
|
||||||
|
void remove_trailing(char c) {
|
||||||
|
while (data[size - 1] == c) --size;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Writes the exponent exp in the form "[+-]d{2,3}" to buffer.
|
||||||
|
template <typename Handler>
|
||||||
|
FMT_FUNC void write_exponent(int exp, Handler &&h) {
|
||||||
|
FMT_ASSERT(-1000 < exp && exp < 1000, "exponent out of range");
|
||||||
|
if (exp < 0) {
|
||||||
|
h.append('-');
|
||||||
|
exp = -exp;
|
||||||
|
} else {
|
||||||
|
h.append('+');
|
||||||
|
}
|
||||||
|
if (exp >= 100) {
|
||||||
|
h.append(static_cast<char>('0' + exp / 100));
|
||||||
|
exp %= 100;
|
||||||
|
const char *d = data::DIGITS + exp * 2;
|
||||||
|
h.append(d[0]);
|
||||||
|
h.append(d[1]);
|
||||||
|
} else {
|
||||||
|
const char *d = data::DIGITS + exp * 2;
|
||||||
|
h.append(d[0]);
|
||||||
|
h.append(d[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct fill {
|
||||||
|
size_t n;
|
||||||
|
void operator()(char *buf) const {
|
||||||
|
buf[0] = '0';
|
||||||
|
buf[1] = '.';
|
||||||
|
std::uninitialized_fill_n(buf + 2, n, '0');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// The number is given as v = f * pow(10, exp), where f has size digits.
|
||||||
|
template <typename Handler>
|
||||||
|
FMT_FUNC void grisu2_prettify(const gen_digits_params ¶ms,
|
||||||
|
int size, int exp, Handler &&handler) {
|
||||||
|
if (!params.fixed) {
|
||||||
|
// Insert a decimal point after the first digit and add an exponent.
|
||||||
|
handler.insert(1, '.');
|
||||||
|
exp += size - 1;
|
||||||
|
if (size < params.num_digits)
|
||||||
|
handler.append(params.num_digits - size, '0');
|
||||||
|
handler.append(params.upper ? 'E' : 'e');
|
||||||
|
write_exponent(exp, handler);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// pow(10, full_exp - 1) <= v <= pow(10, full_exp).
|
||||||
|
int full_exp = size + exp;
|
||||||
|
const int exp_threshold = 21;
|
||||||
|
if (size <= full_exp && full_exp <= exp_threshold) {
|
||||||
|
// 1234e7 -> 12340000000[.0+]
|
||||||
|
handler.append(full_exp - size, '0');
|
||||||
|
int num_zeros = params.num_digits - full_exp;
|
||||||
|
if (num_zeros > 0 && params.trailing_zeros) {
|
||||||
|
handler.append('.');
|
||||||
|
handler.append(num_zeros, '0');
|
||||||
|
}
|
||||||
|
} else if (full_exp > 0) {
|
||||||
|
// 1234e-2 -> 12.34[0+]
|
||||||
|
handler.insert(full_exp, '.');
|
||||||
|
if (!params.trailing_zeros) {
|
||||||
|
// Remove trailing zeros.
|
||||||
|
handler.remove_trailing('0');
|
||||||
|
} else if (params.num_digits > size) {
|
||||||
|
// Add trailing zeros.
|
||||||
|
ptrdiff_t num_zeros = params.num_digits - size;
|
||||||
|
handler.append(num_zeros, '0');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 1234e-6 -> 0.001234
|
||||||
|
handler.insert(0, 2 - full_exp, fill{to_unsigned(-full_exp)});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct char_counter {
|
||||||
|
ptrdiff_t size;
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
void insert(ptrdiff_t, ptrdiff_t n, F) { size += n; }
|
||||||
|
void insert(ptrdiff_t, char) { ++size; }
|
||||||
|
void append(ptrdiff_t n, char) { size += n; }
|
||||||
|
void append(char) { ++size; }
|
||||||
|
void remove_trailing(char) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Converts format specifiers into parameters for digit generation and computes
|
||||||
|
// output buffer size for a number in the range [pow(10, exp - 1), pow(10, exp)
|
||||||
|
// or 0 if exp == 1.
|
||||||
|
FMT_FUNC gen_digits_params process_specs(const core_format_specs &specs,
|
||||||
|
int exp, buffer &buf) {
|
||||||
|
auto params = gen_digits_params();
|
||||||
|
int num_digits = specs.precision >= 0 ? specs.precision : 6;
|
||||||
|
switch (specs.type) {
|
||||||
|
case 'G':
|
||||||
|
params.upper = true;
|
||||||
|
FMT_FALLTHROUGH
|
||||||
|
case '\0': case 'g':
|
||||||
|
params.trailing_zeros = (specs.flags & HASH_FLAG) != 0;
|
||||||
|
if (-4 <= exp && exp < num_digits + 1) {
|
||||||
|
params.fixed = true;
|
||||||
|
if (!specs.type && params.trailing_zeros && exp >= 0)
|
||||||
|
num_digits = exp + 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'F':
|
||||||
|
params.upper = true;
|
||||||
|
FMT_FALLTHROUGH
|
||||||
|
case 'f': {
|
||||||
|
params.fixed = true;
|
||||||
|
params.trailing_zeros = true;
|
||||||
|
int adjusted_min_digits = num_digits + exp;
|
||||||
|
if (adjusted_min_digits > 0)
|
||||||
|
num_digits = adjusted_min_digits;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'E':
|
||||||
|
params.upper = true;
|
||||||
|
FMT_FALLTHROUGH
|
||||||
|
case 'e':
|
||||||
|
++num_digits;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
params.num_digits = num_digits;
|
||||||
|
char_counter counter{num_digits};
|
||||||
|
grisu2_prettify(params, params.num_digits, exp - num_digits, counter);
|
||||||
|
buf.resize(to_unsigned(counter.size));
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Double>
|
||||||
|
FMT_FUNC typename std::enable_if<sizeof(Double) == sizeof(uint64_t), bool>::type
|
||||||
|
grisu2_format(Double value, buffer &buf, core_format_specs specs) {
|
||||||
|
FMT_ASSERT(value >= 0, "value is negative");
|
||||||
|
if (value == 0) {
|
||||||
|
gen_digits_params params = process_specs(specs, 1, buf);
|
||||||
|
const size_t size = 1;
|
||||||
|
buf[0] = '0';
|
||||||
|
grisu2_prettify(params, size, 0, prettify_handler(buf, size));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fp fp_value(value);
|
||||||
|
fp lower, upper; // w^- and w^+ in the Grisu paper.
|
||||||
|
fp_value.compute_boundaries(lower, upper);
|
||||||
|
|
||||||
|
// Find a cached power of 10 close to 1 / upper and use it to scale upper.
|
||||||
|
const int min_exp = -60; // alpha in Grisu.
|
||||||
|
int cached_exp = 0; // K in Grisu.
|
||||||
|
auto cached_pow = get_cached_power( // \tilde{c}_{-k} in Grisu.
|
||||||
|
min_exp - (upper.e + fp::significand_size), cached_exp);
|
||||||
|
cached_exp = -cached_exp;
|
||||||
|
upper = upper * cached_pow; // \tilde{M}^+ in Grisu.
|
||||||
|
--upper.f; // \tilde{M}^+ - 1 ulp -> M^+_{\downarrow}.
|
||||||
|
fp one(1ull << -upper.e, upper.e);
|
||||||
|
// hi (p1 in Grisu) contains the most significant digits of scaled_upper.
|
||||||
|
// hi = floor(upper / one).
|
||||||
|
uint32_t hi = static_cast<uint32_t>(upper.f >> -one.e);
|
||||||
|
int exp = count_digits(hi); // kappa in Grisu.
|
||||||
|
gen_digits_params params = process_specs(specs, cached_exp + exp, buf);
|
||||||
|
fp_value.normalize();
|
||||||
|
fp scaled_value = fp_value * cached_pow;
|
||||||
|
lower = lower * cached_pow; // \tilde{M}^- in Grisu.
|
||||||
|
++lower.f; // \tilde{M}^- + 1 ulp -> M^-_{\uparrow}.
|
||||||
|
uint64_t delta = upper.f - lower.f;
|
||||||
|
fp diff = upper - scaled_value; // wp_w in Grisu.
|
||||||
|
// lo (p2 in Grisu) contains the least significants digits of scaled_upper.
|
||||||
|
// lo = supper % one.
|
||||||
|
uint64_t lo = upper.f & (one.f - 1);
|
||||||
|
int size = 0;
|
||||||
|
if (!grisu2_gen_digits(buf.data(), size, hi, lo, exp, delta, one, diff,
|
||||||
|
params.num_digits)) {
|
||||||
|
buf.clear();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
grisu2_prettify(params, size, cached_exp + exp, prettify_handler(buf, size));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Double>
|
||||||
|
void sprintf_format(Double value, internal::buffer &buf,
|
||||||
|
core_format_specs spec) {
|
||||||
|
// Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail.
|
||||||
|
FMT_ASSERT(buf.capacity() != 0, "empty buffer");
|
||||||
|
|
||||||
|
// Build format string.
|
||||||
|
enum { MAX_FORMAT_SIZE = 10}; // longest format: %#-*.*Lg
|
||||||
|
char format[MAX_FORMAT_SIZE];
|
||||||
|
char *format_ptr = format;
|
||||||
|
*format_ptr++ = '%';
|
||||||
|
if (spec.has(HASH_FLAG))
|
||||||
|
*format_ptr++ = '#';
|
||||||
|
if (spec.precision >= 0) {
|
||||||
|
*format_ptr++ = '.';
|
||||||
|
*format_ptr++ = '*';
|
||||||
|
}
|
||||||
|
if (std::is_same<Double, long double>::value)
|
||||||
|
*format_ptr++ = 'L';
|
||||||
|
*format_ptr++ = spec.type;
|
||||||
|
*format_ptr = '\0';
|
||||||
|
|
||||||
|
// Format using snprintf.
|
||||||
|
char *start = FMT_NULL;
|
||||||
|
for (;;) {
|
||||||
|
std::size_t buffer_size = buf.capacity();
|
||||||
|
start = &buf[0];
|
||||||
|
int result = internal::char_traits<char>::format_float(
|
||||||
|
start, buffer_size, format, spec.precision, value);
|
||||||
|
if (result >= 0) {
|
||||||
|
unsigned n = internal::to_unsigned(result);
|
||||||
|
if (n < buf.capacity()) {
|
||||||
|
buf.resize(n);
|
||||||
|
break; // The buffer is large enough - continue with formatting.
|
||||||
|
}
|
||||||
|
buf.reserve(n + 1);
|
||||||
|
} else {
|
||||||
|
// If result is negative we ask to increase the capacity by at least 1,
|
||||||
|
// but as std::vector, the buffer grows exponentially.
|
||||||
|
buf.reserve(buf.capacity() + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
#if FMT_USE_WINDOWS_H
|
||||||
|
|
||||||
|
FMT_FUNC internal::utf8_to_utf16::utf8_to_utf16(string_view s) {
|
||||||
|
static const char ERROR_MSG[] = "cannot convert string from UTF-8 to UTF-16";
|
||||||
|
if (s.size() > INT_MAX)
|
||||||
|
FMT_THROW(windows_error(ERROR_INVALID_PARAMETER, ERROR_MSG));
|
||||||
|
int s_size = static_cast<int>(s.size());
|
||||||
|
if (s_size == 0) {
|
||||||
|
// MultiByteToWideChar does not support zero length, handle separately.
|
||||||
|
buffer_.resize(1);
|
||||||
|
buffer_[0] = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int length = MultiByteToWideChar(
|
||||||
|
CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, FMT_NULL, 0);
|
||||||
|
if (length == 0)
|
||||||
|
FMT_THROW(windows_error(GetLastError(), ERROR_MSG));
|
||||||
|
buffer_.resize(length + 1);
|
||||||
|
length = MultiByteToWideChar(
|
||||||
|
CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, &buffer_[0], length);
|
||||||
|
if (length == 0)
|
||||||
|
FMT_THROW(windows_error(GetLastError(), ERROR_MSG));
|
||||||
|
buffer_[length] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_FUNC internal::utf16_to_utf8::utf16_to_utf8(wstring_view s) {
|
||||||
|
if (int error_code = convert(s)) {
|
||||||
|
FMT_THROW(windows_error(error_code,
|
||||||
|
"cannot convert string from UTF-16 to UTF-8"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_FUNC int internal::utf16_to_utf8::convert(wstring_view s) {
|
||||||
|
if (s.size() > INT_MAX)
|
||||||
|
return ERROR_INVALID_PARAMETER;
|
||||||
|
int s_size = static_cast<int>(s.size());
|
||||||
|
if (s_size == 0) {
|
||||||
|
// WideCharToMultiByte does not support zero length, handle separately.
|
||||||
|
buffer_.resize(1);
|
||||||
|
buffer_[0] = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int length = WideCharToMultiByte(
|
||||||
|
CP_UTF8, 0, s.data(), s_size, FMT_NULL, 0, FMT_NULL, FMT_NULL);
|
||||||
|
if (length == 0)
|
||||||
|
return GetLastError();
|
||||||
|
buffer_.resize(length + 1);
|
||||||
|
length = WideCharToMultiByte(
|
||||||
|
CP_UTF8, 0, s.data(), s_size, &buffer_[0], length, FMT_NULL, FMT_NULL);
|
||||||
|
if (length == 0)
|
||||||
|
return GetLastError();
|
||||||
|
buffer_[length] = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_FUNC void windows_error::init(
|
||||||
|
int err_code, string_view format_str, format_args args) {
|
||||||
|
error_code_ = err_code;
|
||||||
|
memory_buffer buffer;
|
||||||
|
internal::format_windows_error(buffer, err_code, vformat(format_str, args));
|
||||||
|
std::runtime_error &base = *this;
|
||||||
|
base = std::runtime_error(to_string(buffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_FUNC void internal::format_windows_error(
|
||||||
|
internal::buffer &out, int error_code, string_view message) FMT_NOEXCEPT {
|
||||||
|
FMT_TRY {
|
||||||
|
wmemory_buffer buf;
|
||||||
|
buf.resize(inline_buffer_size);
|
||||||
|
for (;;) {
|
||||||
|
wchar_t *system_message = &buf[0];
|
||||||
|
int result = FormatMessageW(
|
||||||
|
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||||
|
FMT_NULL, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||||
|
system_message, static_cast<uint32_t>(buf.size()), FMT_NULL);
|
||||||
|
if (result != 0) {
|
||||||
|
utf16_to_utf8 utf8_message;
|
||||||
|
if (utf8_message.convert(system_message) == ERROR_SUCCESS) {
|
||||||
|
writer w(out);
|
||||||
|
w.write(message);
|
||||||
|
w.write(": ");
|
||||||
|
w.write(utf8_message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
|
||||||
|
break; // Can't get error message, report error code instead.
|
||||||
|
buf.resize(buf.size() * 2);
|
||||||
|
}
|
||||||
|
} FMT_CATCH(...) {}
|
||||||
|
format_error_code(out, error_code, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // FMT_USE_WINDOWS_H
|
||||||
|
|
||||||
|
FMT_FUNC void format_system_error(
|
||||||
|
internal::buffer &out, int error_code, string_view message) FMT_NOEXCEPT {
|
||||||
|
FMT_TRY {
|
||||||
|
memory_buffer buf;
|
||||||
|
buf.resize(inline_buffer_size);
|
||||||
|
for (;;) {
|
||||||
|
char *system_message = &buf[0];
|
||||||
|
int result = safe_strerror(error_code, system_message, buf.size());
|
||||||
|
if (result == 0) {
|
||||||
|
writer w(out);
|
||||||
|
w.write(message);
|
||||||
|
w.write(": ");
|
||||||
|
w.write(system_message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (result != ERANGE)
|
||||||
|
break; // Can't get error message, report error code instead.
|
||||||
|
buf.resize(buf.size() * 2);
|
||||||
|
}
|
||||||
|
} FMT_CATCH(...) {}
|
||||||
|
format_error_code(out, error_code, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_FUNC void internal::error_handler::on_error(const char *message) {
|
||||||
|
FMT_THROW(format_error(message));
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_FUNC void report_system_error(
|
||||||
|
int error_code, fmt::string_view message) FMT_NOEXCEPT {
|
||||||
|
report_error(format_system_error, error_code, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if FMT_USE_WINDOWS_H
|
||||||
|
FMT_FUNC void report_windows_error(
|
||||||
|
int error_code, fmt::string_view message) FMT_NOEXCEPT {
|
||||||
|
report_error(internal::format_windows_error, error_code, message);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
FMT_FUNC void vprint(std::FILE *f, string_view format_str, format_args args) {
|
||||||
|
memory_buffer buffer;
|
||||||
|
internal::vformat_to(buffer, format_str,
|
||||||
|
basic_format_args<buffer_context<char>::type>(args));
|
||||||
|
std::fwrite(buffer.data(), 1, buffer.size(), f);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_FUNC void vprint(std::FILE *f, wstring_view format_str, wformat_args args) {
|
||||||
|
wmemory_buffer buffer;
|
||||||
|
internal::vformat_to(buffer, format_str, args);
|
||||||
|
std::fwrite(buffer.data(), sizeof(wchar_t), buffer.size(), f);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_FUNC void vprint(string_view format_str, format_args args) {
|
||||||
|
vprint(stdout, format_str, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_FUNC void vprint(wstring_view format_str, wformat_args args) {
|
||||||
|
vprint(stdout, format_str, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
# pragma warning(pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // FMT_FORMAT_INL_H_
|
3555
third_party/spdlog/include/spdlog/fmt/bundled/format.h
vendored
Normal file
3555
third_party/spdlog/include/spdlog/fmt/bundled/format.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
77
third_party/spdlog/include/spdlog/fmt/bundled/locale.h
vendored
Normal file
77
third_party/spdlog/include/spdlog/fmt/bundled/locale.h
vendored
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
// Formatting library for C++ - std::locale support
|
||||||
|
//
|
||||||
|
// Copyright (c) 2012 - present, Victor Zverovich
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
|
#ifndef FMT_LOCALE_H_
|
||||||
|
#define FMT_LOCALE_H_
|
||||||
|
|
||||||
|
#include "format.h"
|
||||||
|
#include <locale>
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
template <typename Char>
|
||||||
|
typename buffer_context<Char>::type::iterator vformat_to(
|
||||||
|
const std::locale &loc, basic_buffer<Char> &buf,
|
||||||
|
basic_string_view<Char> format_str,
|
||||||
|
basic_format_args<typename buffer_context<Char>::type> args) {
|
||||||
|
typedef back_insert_range<basic_buffer<Char> > range;
|
||||||
|
return vformat_to<arg_formatter<range>>(
|
||||||
|
buf, to_string_view(format_str), args, internal::locale_ref(loc));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
std::basic_string<Char> vformat(
|
||||||
|
const std::locale &loc, basic_string_view<Char> format_str,
|
||||||
|
basic_format_args<typename buffer_context<Char>::type> args) {
|
||||||
|
basic_memory_buffer<Char> buffer;
|
||||||
|
internal::vformat_to(loc, buffer, format_str, args);
|
||||||
|
return fmt::to_string(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename S, typename Char = FMT_CHAR(S)>
|
||||||
|
inline std::basic_string<Char> vformat(
|
||||||
|
const std::locale &loc, const S &format_str,
|
||||||
|
basic_format_args<typename buffer_context<Char>::type> args) {
|
||||||
|
return internal::vformat(loc, to_string_view(format_str), args);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename S, typename... Args>
|
||||||
|
inline std::basic_string<FMT_CHAR(S)> format(
|
||||||
|
const std::locale &loc, const S &format_str, const Args &... args) {
|
||||||
|
return internal::vformat(
|
||||||
|
loc, to_string_view(format_str),
|
||||||
|
*internal::checked_args<S, Args...>(format_str, args...));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename String, typename OutputIt, typename... Args>
|
||||||
|
inline typename std::enable_if<internal::is_output_iterator<OutputIt>::value,
|
||||||
|
OutputIt>::type
|
||||||
|
vformat_to(OutputIt out, const std::locale &loc, const String &format_str,
|
||||||
|
typename format_args_t<OutputIt, FMT_CHAR(String)>::type args) {
|
||||||
|
typedef output_range<OutputIt, FMT_CHAR(String)> range;
|
||||||
|
return vformat_to<arg_formatter<range>>(
|
||||||
|
range(out), to_string_view(format_str), args, internal::locale_ref(loc));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt, typename S, typename... Args>
|
||||||
|
inline typename std::enable_if<
|
||||||
|
internal::is_string<S>::value &&
|
||||||
|
internal::is_output_iterator<OutputIt>::value, OutputIt>::type
|
||||||
|
format_to(OutputIt out, const std::locale &loc, const S &format_str,
|
||||||
|
const Args &... args) {
|
||||||
|
internal::check_format_string<Args...>(format_str);
|
||||||
|
typedef typename format_context_t<OutputIt, FMT_CHAR(S)>::type context;
|
||||||
|
format_arg_store<context, Args...> as{args...};
|
||||||
|
return vformat_to(out, loc, to_string_view(format_str),
|
||||||
|
basic_format_args<context>(as));
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // FMT_LOCALE_H_
|
153
third_party/spdlog/include/spdlog/fmt/bundled/ostream.h
vendored
Normal file
153
third_party/spdlog/include/spdlog/fmt/bundled/ostream.h
vendored
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
// Formatting library for C++ - std::ostream support
|
||||||
|
//
|
||||||
|
// Copyright (c) 2012 - present, Victor Zverovich
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
|
#ifndef FMT_OSTREAM_H_
|
||||||
|
#define FMT_OSTREAM_H_
|
||||||
|
|
||||||
|
#include "format.h"
|
||||||
|
#include <ostream>
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
template <class Char>
|
||||||
|
class formatbuf : public std::basic_streambuf<Char> {
|
||||||
|
private:
|
||||||
|
typedef typename std::basic_streambuf<Char>::int_type int_type;
|
||||||
|
typedef typename std::basic_streambuf<Char>::traits_type traits_type;
|
||||||
|
|
||||||
|
basic_buffer<Char> &buffer_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
formatbuf(basic_buffer<Char> &buffer) : buffer_(buffer) {}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// The put-area is actually always empty. This makes the implementation
|
||||||
|
// simpler and has the advantage that the streambuf and the buffer are always
|
||||||
|
// in sync and sputc never writes into uninitialized memory. The obvious
|
||||||
|
// disadvantage is that each call to sputc always results in a (virtual) call
|
||||||
|
// to overflow. There is no disadvantage here for sputn since this always
|
||||||
|
// results in a call to xsputn.
|
||||||
|
|
||||||
|
int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE {
|
||||||
|
if (!traits_type::eq_int_type(ch, traits_type::eof()))
|
||||||
|
buffer_.push_back(static_cast<Char>(ch));
|
||||||
|
return ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::streamsize xsputn(const Char *s, std::streamsize count) FMT_OVERRIDE {
|
||||||
|
buffer_.append(s, s + count);
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
struct test_stream : std::basic_ostream<Char> {
|
||||||
|
private:
|
||||||
|
struct null;
|
||||||
|
// Hide all operator<< from std::basic_ostream<Char>.
|
||||||
|
void operator<<(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Checks if T has a user-defined operator<< (e.g. not a member of std::ostream).
|
||||||
|
template <typename T, typename Char>
|
||||||
|
class is_streamable {
|
||||||
|
private:
|
||||||
|
template <typename U>
|
||||||
|
static decltype(
|
||||||
|
internal::declval<test_stream<Char>&>()
|
||||||
|
<< internal::declval<U>(), std::true_type()) test(int);
|
||||||
|
|
||||||
|
template <typename>
|
||||||
|
static std::false_type test(...);
|
||||||
|
|
||||||
|
typedef decltype(test<T>(0)) result;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static const bool value = result::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Write the content of buf to os.
|
||||||
|
template <typename Char>
|
||||||
|
void write(std::basic_ostream<Char> &os, basic_buffer<Char> &buf) {
|
||||||
|
const Char *data = buf.data();
|
||||||
|
typedef std::make_unsigned<std::streamsize>::type UnsignedStreamSize;
|
||||||
|
UnsignedStreamSize size = buf.size();
|
||||||
|
UnsignedStreamSize max_size =
|
||||||
|
internal::to_unsigned((std::numeric_limits<std::streamsize>::max)());
|
||||||
|
do {
|
||||||
|
UnsignedStreamSize n = size <= max_size ? size : max_size;
|
||||||
|
os.write(data, static_cast<std::streamsize>(n));
|
||||||
|
data += n;
|
||||||
|
size -= n;
|
||||||
|
} while (size != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char, typename T>
|
||||||
|
void format_value(basic_buffer<Char> &buffer, const T &value) {
|
||||||
|
internal::formatbuf<Char> format_buf(buffer);
|
||||||
|
std::basic_ostream<Char> output(&format_buf);
|
||||||
|
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
|
||||||
|
output << value;
|
||||||
|
buffer.resize(buffer.size());
|
||||||
|
}
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
// Disable conversion to int if T has an overloaded operator<< which is a free
|
||||||
|
// function (not a member of std::ostream).
|
||||||
|
template <typename T, typename Char>
|
||||||
|
struct convert_to_int<T, Char, void> {
|
||||||
|
static const bool value =
|
||||||
|
convert_to_int<T, Char, int>::value &&
|
||||||
|
!internal::is_streamable<T, Char>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Formats an object of type T that has an overloaded ostream operator<<.
|
||||||
|
template <typename T, typename Char>
|
||||||
|
struct formatter<T, Char,
|
||||||
|
typename std::enable_if<
|
||||||
|
internal::is_streamable<T, Char>::value &&
|
||||||
|
!internal::format_type<
|
||||||
|
typename buffer_context<Char>::type, T>::value>::type>
|
||||||
|
: formatter<basic_string_view<Char>, Char> {
|
||||||
|
|
||||||
|
template <typename Context>
|
||||||
|
auto format(const T &value, Context &ctx) -> decltype(ctx.out()) {
|
||||||
|
basic_memory_buffer<Char> buffer;
|
||||||
|
internal::format_value(buffer, value);
|
||||||
|
basic_string_view<Char> str(buffer.data(), buffer.size());
|
||||||
|
return formatter<basic_string_view<Char>, Char>::format(str, ctx);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
inline void vprint(std::basic_ostream<Char> &os,
|
||||||
|
basic_string_view<Char> format_str,
|
||||||
|
basic_format_args<typename buffer_context<Char>::type> args) {
|
||||||
|
basic_memory_buffer<Char> buffer;
|
||||||
|
internal::vformat_to(buffer, format_str, args);
|
||||||
|
internal::write(os, buffer);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Prints formatted data to the stream *os*.
|
||||||
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
|
fmt::print(cerr, "Don't {}!", "panic");
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename S, typename... Args>
|
||||||
|
inline typename std::enable_if<internal::is_string<S>::value>::type
|
||||||
|
print(std::basic_ostream<FMT_CHAR(S)> &os, const S &format_str,
|
||||||
|
const Args & ... args) {
|
||||||
|
internal::checked_args<S, Args...> ca(format_str, args...);
|
||||||
|
vprint(os, to_string_view(format_str), *ca);
|
||||||
|
}
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // FMT_OSTREAM_H_
|
324
third_party/spdlog/include/spdlog/fmt/bundled/posix.h
vendored
Normal file
324
third_party/spdlog/include/spdlog/fmt/bundled/posix.h
vendored
Normal file
@ -0,0 +1,324 @@
|
|||||||
|
// A C++ interface to POSIX functions.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2012 - 2016, Victor Zverovich
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
|
#ifndef FMT_POSIX_H_
|
||||||
|
#define FMT_POSIX_H_
|
||||||
|
|
||||||
|
#if defined(__MINGW32__) || defined(__CYGWIN__)
|
||||||
|
// Workaround MinGW bug https://sourceforge.net/p/mingw/bugs/2024/.
|
||||||
|
# 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 <cstddef>
|
||||||
|
|
||||||
|
#if defined __APPLE__ || defined(__FreeBSD__)
|
||||||
|
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "format.h"
|
||||||
|
|
||||||
|
#ifndef FMT_POSIX
|
||||||
|
# if defined(_WIN32) && !defined(__MINGW32__)
|
||||||
|
// Fix warnings about deprecated symbols.
|
||||||
|
# define FMT_POSIX(call) _##call
|
||||||
|
# else
|
||||||
|
# define FMT_POSIX(call) call
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Calls to system functions are wrapped in FMT_SYSTEM for testability.
|
||||||
|
#ifdef FMT_SYSTEM
|
||||||
|
# define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
|
||||||
|
#else
|
||||||
|
# define FMT_SYSTEM(call) call
|
||||||
|
# ifdef _WIN32
|
||||||
|
// Fix warnings about deprecated symbols.
|
||||||
|
# define FMT_POSIX_CALL(call) ::_##call
|
||||||
|
# else
|
||||||
|
# define FMT_POSIX_CALL(call) ::call
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Retries the expression while it evaluates to error_result and errno
|
||||||
|
// equals to EINTR.
|
||||||
|
#ifndef _WIN32
|
||||||
|
# define FMT_RETRY_VAL(result, expression, error_result) \
|
||||||
|
do { \
|
||||||
|
result = (expression); \
|
||||||
|
} while (result == error_result && errno == EINTR)
|
||||||
|
#else
|
||||||
|
# define FMT_RETRY_VAL(result, expression, error_result) result = (expression)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
A reference to a null-terminated string. It can be constructed from a C
|
||||||
|
string or ``std::string``.
|
||||||
|
|
||||||
|
You can use one of the following typedefs for common character types:
|
||||||
|
|
||||||
|
+---------------+-----------------------------+
|
||||||
|
| Type | Definition |
|
||||||
|
+===============+=============================+
|
||||||
|
| cstring_view | basic_cstring_view<char> |
|
||||||
|
+---------------+-----------------------------+
|
||||||
|
| wcstring_view | basic_cstring_view<wchar_t> |
|
||||||
|
+---------------+-----------------------------+
|
||||||
|
|
||||||
|
This class is most useful as a parameter type to allow passing
|
||||||
|
different types of strings to a function, for example::
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
std::string format(cstring_view format_str, const Args & ... args);
|
||||||
|
|
||||||
|
format("{}", 42);
|
||||||
|
format(std::string("{}"), 42);
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename Char>
|
||||||
|
class basic_cstring_view {
|
||||||
|
private:
|
||||||
|
const Char *data_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/** Constructs a string reference object from a C string. */
|
||||||
|
basic_cstring_view(const Char *s) : data_(s) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Constructs a string reference from an ``std::string`` object.
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
basic_cstring_view(const std::basic_string<Char> &s) : data_(s.c_str()) {}
|
||||||
|
|
||||||
|
/** Returns the pointer to a C string. */
|
||||||
|
const Char *c_str() const { return data_; }
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef basic_cstring_view<char> cstring_view;
|
||||||
|
typedef basic_cstring_view<wchar_t> wcstring_view;
|
||||||
|
|
||||||
|
// An error code.
|
||||||
|
class error_code {
|
||||||
|
private:
|
||||||
|
int value_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit error_code(int value = 0) FMT_NOEXCEPT : value_(value) {}
|
||||||
|
|
||||||
|
int get() const FMT_NOEXCEPT { return value_; }
|
||||||
|
};
|
||||||
|
|
||||||
|
// A buffered file.
|
||||||
|
class buffered_file {
|
||||||
|
private:
|
||||||
|
FILE *file_;
|
||||||
|
|
||||||
|
friend class file;
|
||||||
|
|
||||||
|
explicit buffered_file(FILE *f) : file_(f) {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Constructs a buffered_file object which doesn't represent any file.
|
||||||
|
buffered_file() FMT_NOEXCEPT : file_(FMT_NULL) {}
|
||||||
|
|
||||||
|
// 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_ = FMT_NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffered_file& operator=(buffered_file &&other) {
|
||||||
|
close();
|
||||||
|
file_ = other.file_;
|
||||||
|
other.file_ = FMT_NULL;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Opens a file.
|
||||||
|
FMT_API buffered_file(cstring_view filename, cstring_view mode);
|
||||||
|
|
||||||
|
// Closes the file.
|
||||||
|
FMT_API void close();
|
||||||
|
|
||||||
|
// Returns the pointer to a FILE object representing this file.
|
||||||
|
FILE *get() const FMT_NOEXCEPT { return file_; }
|
||||||
|
|
||||||
|
// We place parentheses around fileno to workaround a bug in some versions
|
||||||
|
// of MinGW that define fileno as a macro.
|
||||||
|
FMT_API int (fileno)() const;
|
||||||
|
|
||||||
|
void vprint(string_view format_str, format_args args) {
|
||||||
|
fmt::vprint(file_, format_str, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
inline void print(string_view format_str, const Args & ... args) {
|
||||||
|
vprint(format_str, make_format_args(args...));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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
|
||||||
|
// closing the file multiple times will cause a crash on Windows rather
|
||||||
|
// than an exception. You can get standard behavior by overriding the
|
||||||
|
// invalid parameter handler with _set_invalid_parameter_handler.
|
||||||
|
class file {
|
||||||
|
private:
|
||||||
|
int fd_; // File descriptor.
|
||||||
|
|
||||||
|
// Constructs a file object with a given descriptor.
|
||||||
|
explicit file(int fd) : fd_(fd) {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Possible values for the oflag argument to the constructor.
|
||||||
|
enum {
|
||||||
|
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
|
||||||
|
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
|
||||||
|
RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing.
|
||||||
|
};
|
||||||
|
|
||||||
|
// Constructs a file object which doesn't represent any file.
|
||||||
|
file() FMT_NOEXCEPT : fd_(-1) {}
|
||||||
|
|
||||||
|
// Opens a file and constructs a file object representing this file.
|
||||||
|
FMT_API file(cstring_view path, int oflag);
|
||||||
|
|
||||||
|
private:
|
||||||
|
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) {
|
||||||
|
close();
|
||||||
|
fd_ = other.fd_;
|
||||||
|
other.fd_ = -1;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destroys the object closing the file it represents if any.
|
||||||
|
FMT_API ~file() FMT_NOEXCEPT;
|
||||||
|
|
||||||
|
// Returns the file descriptor.
|
||||||
|
int descriptor() const FMT_NOEXCEPT { return fd_; }
|
||||||
|
|
||||||
|
// Closes the file.
|
||||||
|
FMT_API void close();
|
||||||
|
|
||||||
|
// Returns the file size. The size has signed type for consistency with
|
||||||
|
// stat::st_size.
|
||||||
|
FMT_API long long size() const;
|
||||||
|
|
||||||
|
// Attempts to read count bytes from the file into the specified buffer.
|
||||||
|
FMT_API std::size_t read(void *buffer, std::size_t count);
|
||||||
|
|
||||||
|
// Attempts to write count bytes from the specified buffer to the file.
|
||||||
|
FMT_API std::size_t write(const void *buffer, std::size_t count);
|
||||||
|
|
||||||
|
// Duplicates a file descriptor with the dup function and returns
|
||||||
|
// the duplicate as a file object.
|
||||||
|
FMT_API static file dup(int fd);
|
||||||
|
|
||||||
|
// Makes fd be the copy of this file descriptor, closing fd first if
|
||||||
|
// necessary.
|
||||||
|
FMT_API void dup2(int fd);
|
||||||
|
|
||||||
|
// Makes fd be the copy of this file descriptor, closing fd first if
|
||||||
|
// necessary.
|
||||||
|
FMT_API void dup2(int fd, error_code &ec) FMT_NOEXCEPT;
|
||||||
|
|
||||||
|
// Creates a pipe setting up read_end and write_end file objects for reading
|
||||||
|
// and writing respectively.
|
||||||
|
FMT_API static void pipe(file &read_end, file &write_end);
|
||||||
|
|
||||||
|
// Creates a buffered_file object associated with this file and detaches
|
||||||
|
// this file object from the file.
|
||||||
|
FMT_API buffered_file fdopen(const char *mode);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Returns the memory page size.
|
||||||
|
long getpagesize();
|
||||||
|
|
||||||
|
#if (defined(LC_NUMERIC_MASK) || defined(_MSC_VER)) && \
|
||||||
|
!defined(__ANDROID__) && !defined(__CYGWIN__) && !defined(__OpenBSD__) && \
|
||||||
|
!defined(__NEWLIB_H__)
|
||||||
|
# define FMT_LOCALE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef FMT_LOCALE
|
||||||
|
// A "C" numeric locale.
|
||||||
|
class Locale {
|
||||||
|
private:
|
||||||
|
# ifdef _MSC_VER
|
||||||
|
typedef _locale_t locale_t;
|
||||||
|
|
||||||
|
enum { LC_NUMERIC_MASK = LC_NUMERIC };
|
||||||
|
|
||||||
|
static locale_t newlocale(int category_mask, const char *locale, locale_t) {
|
||||||
|
return _create_locale(category_mask, locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void freelocale(locale_t locale) {
|
||||||
|
_free_locale(locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
static double strtod_l(const char *nptr, char **endptr, _locale_t locale) {
|
||||||
|
return _strtod_l(nptr, endptr, locale);
|
||||||
|
}
|
||||||
|
# endif
|
||||||
|
|
||||||
|
locale_t locale_;
|
||||||
|
|
||||||
|
Locale(const Locale &) = delete;
|
||||||
|
void operator=(const Locale &) = delete;
|
||||||
|
|
||||||
|
public:
|
||||||
|
typedef locale_t Type;
|
||||||
|
|
||||||
|
Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", FMT_NULL)) {
|
||||||
|
if (!locale_)
|
||||||
|
FMT_THROW(system_error(errno, "cannot create locale"));
|
||||||
|
}
|
||||||
|
~Locale() { freelocale(locale_); }
|
||||||
|
|
||||||
|
Type get() const { return locale_; }
|
||||||
|
|
||||||
|
// Converts string to floating-point number and advances str past the end
|
||||||
|
// of the parsed input.
|
||||||
|
double strtod(const char *&str) const {
|
||||||
|
char *end = FMT_NULL;
|
||||||
|
double result = strtod_l(str, &end, locale_);
|
||||||
|
str = end;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#endif // FMT_LOCALE
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // FMT_POSIX_H_
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user