@@ -5,3 +5,8 @@ repos:
|
||||
- id: check-yaml
|
||||
- id: end-of-file-fixer
|
||||
- id: trailing-whitespace
|
||||
- repo: https://github.com/pocc/pre-commit-hooks
|
||||
rev: master
|
||||
hooks:
|
||||
- id: clang-format
|
||||
args: [-i, -style=file]
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
*/
|
||||
|
||||
#include "IXCobraBot.h"
|
||||
#include "IXQueueManager.h"
|
||||
|
||||
#include "IXQueueManager.h"
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <ixcobra/IXCobraConnection.h>
|
||||
@@ -109,7 +109,6 @@ namespace ix
|
||||
|
||||
auto sender =
|
||||
[this, &queueManager, verbose, &sentCount, &stop, &throttled, &fatalCobraError] {
|
||||
|
||||
while (true)
|
||||
{
|
||||
auto data = queueManager.pop();
|
||||
@@ -119,7 +118,8 @@ namespace ix
|
||||
if (stop) break;
|
||||
if (msg.isNull()) continue;
|
||||
|
||||
if (_onBotMessageCallback && _onBotMessageCallback(msg, position, verbose, throttled, fatalCobraError))
|
||||
if (_onBotMessageCallback &&
|
||||
_onBotMessageCallback(msg, position, verbose, throttled, fatalCobraError))
|
||||
{
|
||||
// That might be too noisy
|
||||
if (verbose)
|
||||
@@ -155,8 +155,7 @@ namespace ix
|
||||
&fatalCobraError,
|
||||
&useQueue,
|
||||
&queueManager,
|
||||
&sentCount](const CobraEventPtr& event)
|
||||
{
|
||||
&sentCount](const CobraEventPtr& event) {
|
||||
if (event->type == ix::CobraEventType::Open)
|
||||
{
|
||||
spdlog::info("Subscriber connected");
|
||||
@@ -178,11 +177,21 @@ namespace ix
|
||||
conn.subscribe(channel,
|
||||
filter,
|
||||
subscriptionPosition,
|
||||
[this, &jsonWriter, verbose, &throttled, &receivedCount, &queueManager, &useQueue, &subscriptionPosition, &fatalCobraError, &sentCount](
|
||||
const Json::Value& msg, const std::string& position) {
|
||||
[this,
|
||||
&jsonWriter,
|
||||
verbose,
|
||||
&throttled,
|
||||
&receivedCount,
|
||||
&queueManager,
|
||||
&useQueue,
|
||||
&subscriptionPosition,
|
||||
&fatalCobraError,
|
||||
&sentCount](const Json::Value& msg, const std::string& position) {
|
||||
if (verbose)
|
||||
{
|
||||
spdlog::info("Subscriber received message {} -> {}", position, jsonWriter.write(msg));
|
||||
spdlog::info("Subscriber received message {} -> {}",
|
||||
position,
|
||||
jsonWriter.write(msg));
|
||||
}
|
||||
|
||||
subscriptionPosition = position;
|
||||
@@ -201,7 +210,9 @@ namespace ix
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_onBotMessageCallback && _onBotMessageCallback(msg, position, verbose, throttled, fatalCobraError))
|
||||
if (_onBotMessageCallback &&
|
||||
_onBotMessageCallback(
|
||||
msg, position, verbose, throttled, fatalCobraError))
|
||||
{
|
||||
// That might be too noisy
|
||||
if (verbose)
|
||||
@@ -268,7 +279,7 @@ namespace ix
|
||||
// Run for a duration, used by unittesting now
|
||||
else
|
||||
{
|
||||
for (int i = 0 ; i < runtime; ++i)
|
||||
for (int i = 0; i < runtime; ++i)
|
||||
{
|
||||
auto duration = std::chrono::seconds(1);
|
||||
std::this_thread::sleep_for(duration);
|
||||
@@ -300,4 +311,4 @@ namespace ix
|
||||
{
|
||||
_onBotMessageCallback = callback;
|
||||
}
|
||||
}
|
||||
} // namespace ix
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ixcobra/IXCobraConfig.h>
|
||||
#include <stddef.h>
|
||||
#include <json/json.h>
|
||||
#include <functional>
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <ixcobra/IXCobraConfig.h>
|
||||
#include <json/json.h>
|
||||
#include <stddef.h>
|
||||
|
||||
namespace ix
|
||||
{
|
||||
@@ -40,4 +40,4 @@ namespace ix
|
||||
private:
|
||||
OnBotMessageCallback _onBotMessageCallback;
|
||||
};
|
||||
}
|
||||
} // namespace ix
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
*/
|
||||
|
||||
#include "IXCobraToSentryBot.h"
|
||||
|
||||
#include "IXCobraBot.h"
|
||||
#include "IXQueueManager.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <ixcobra/IXCobraConnection.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
@@ -31,7 +31,8 @@ namespace ix
|
||||
const std::string& /*position*/,
|
||||
const bool verbose,
|
||||
std::atomic<bool>& throttled,
|
||||
std::atomic<bool>& /*fatalCobraError*/) -> bool {
|
||||
std::atomic<bool> &
|
||||
/*fatalCobraError*/) -> bool {
|
||||
auto ret = sentryClient.send(msg, verbose);
|
||||
HttpResponsePtr response = ret.first;
|
||||
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <ixcobra/IXCobraConfig.h>
|
||||
#include <ixsentry/IXSentryClient.h>
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
|
||||
namespace ix
|
||||
{
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
*/
|
||||
|
||||
#include "IXCobraToStatsdBot.h"
|
||||
|
||||
#include "IXCobraBot.h"
|
||||
#include "IXQueueManager.h"
|
||||
#include "IXStatsdClient.h"
|
||||
#include "IXCobraBot.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <ixcobra/IXCobraConnection.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
@@ -74,73 +74,74 @@ namespace ix
|
||||
auto tokens = parseFields(fields);
|
||||
|
||||
CobraBot bot;
|
||||
bot.setOnBotMessageCallback([&statsdClient, &tokens, &gauge, &timer](const Json::Value& msg,
|
||||
const std::string& /*position*/,
|
||||
const bool verbose,
|
||||
std::atomic<bool>& /*throttled*/,
|
||||
std::atomic<bool>& fatalCobraError) -> bool {
|
||||
std::string id;
|
||||
for (auto&& attr : tokens)
|
||||
{
|
||||
id += ".";
|
||||
auto val = extractAttr(attr, msg);
|
||||
id += val.asString();
|
||||
}
|
||||
bot.setOnBotMessageCallback(
|
||||
[&statsdClient, &tokens, &gauge, &timer](const Json::Value& msg,
|
||||
const std::string& /*position*/,
|
||||
const bool verbose,
|
||||
std::atomic<bool>& /*throttled*/,
|
||||
std::atomic<bool>& fatalCobraError) -> bool {
|
||||
std::string id;
|
||||
for (auto&& attr : tokens)
|
||||
{
|
||||
id += ".";
|
||||
auto val = extractAttr(attr, msg);
|
||||
id += val.asString();
|
||||
}
|
||||
|
||||
if (gauge.empty() && timer.empty())
|
||||
{
|
||||
statsdClient.count(id, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string attrName = (!gauge.empty()) ? gauge : timer;
|
||||
auto val = extractAttr(attrName, msg);
|
||||
size_t x;
|
||||
|
||||
if (val.isInt())
|
||||
if (gauge.empty() && timer.empty())
|
||||
{
|
||||
x = (size_t) val.asInt();
|
||||
}
|
||||
else if (val.isInt64())
|
||||
{
|
||||
x = (size_t) val.asInt64();
|
||||
}
|
||||
else if (val.isUInt())
|
||||
{
|
||||
x = (size_t) val.asUInt();
|
||||
}
|
||||
else if (val.isUInt64())
|
||||
{
|
||||
x = (size_t) val.asUInt64();
|
||||
}
|
||||
else if (val.isDouble())
|
||||
{
|
||||
x = (size_t) val.asUInt64();
|
||||
statsdClient.count(id, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
spdlog::error("Gauge {} is not a numeric type", gauge);
|
||||
fatalCobraError = true;
|
||||
return false;
|
||||
std::string attrName = (!gauge.empty()) ? gauge : timer;
|
||||
auto val = extractAttr(attrName, msg);
|
||||
size_t x;
|
||||
|
||||
if (val.isInt())
|
||||
{
|
||||
x = (size_t) val.asInt();
|
||||
}
|
||||
else if (val.isInt64())
|
||||
{
|
||||
x = (size_t) val.asInt64();
|
||||
}
|
||||
else if (val.isUInt())
|
||||
{
|
||||
x = (size_t) val.asUInt();
|
||||
}
|
||||
else if (val.isUInt64())
|
||||
{
|
||||
x = (size_t) val.asUInt64();
|
||||
}
|
||||
else if (val.isDouble())
|
||||
{
|
||||
x = (size_t) val.asUInt64();
|
||||
}
|
||||
else
|
||||
{
|
||||
spdlog::error("Gauge {} is not a numeric type", gauge);
|
||||
fatalCobraError = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (verbose)
|
||||
{
|
||||
spdlog::info("{} - {} -> {}", id, attrName, x);
|
||||
}
|
||||
|
||||
if (!gauge.empty())
|
||||
{
|
||||
statsdClient.gauge(id, x);
|
||||
}
|
||||
else
|
||||
{
|
||||
statsdClient.timing(id, x);
|
||||
}
|
||||
}
|
||||
|
||||
if (verbose)
|
||||
{
|
||||
spdlog::info("{} - {} -> {}", id, attrName, x);
|
||||
}
|
||||
|
||||
if (!gauge.empty())
|
||||
{
|
||||
statsdClient.gauge(id, x);
|
||||
}
|
||||
else
|
||||
{
|
||||
statsdClient.timing(id, x);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
return true;
|
||||
});
|
||||
|
||||
bool useQueue = true;
|
||||
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <ixcobra/IXCobraConfig.h>
|
||||
#include <ixbots/IXStatsdClient.h>
|
||||
#include <string>
|
||||
#include <stddef.h>
|
||||
#include <cstdint>
|
||||
#include <ixbots/IXStatsdClient.h>
|
||||
#include <ixcobra/IXCobraConfig.h>
|
||||
#include <stddef.h>
|
||||
#include <string>
|
||||
|
||||
namespace ix
|
||||
{
|
||||
|
||||
@@ -5,13 +5,13 @@
|
||||
*/
|
||||
|
||||
#include "IXCobraToStdoutBot.h"
|
||||
|
||||
#include "IXCobraBot.h"
|
||||
#include "IXQueueManager.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
|
||||
namespace ix
|
||||
{
|
||||
@@ -79,17 +79,19 @@ namespace ix
|
||||
CobraBot bot;
|
||||
auto jsonWriter = makeStreamWriter();
|
||||
|
||||
bot.setOnBotMessageCallback([&fluentd, &quiet, &jsonWriter](const Json::Value& msg,
|
||||
const std::string& position,
|
||||
const bool /*verbose*/,
|
||||
std::atomic<bool>& /*throttled*/,
|
||||
std::atomic<bool>& /*fatalCobraError*/) -> bool {
|
||||
if (!quiet)
|
||||
{
|
||||
writeToStdout(fluentd, jsonWriter, msg, position);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
bot.setOnBotMessageCallback(
|
||||
[&fluentd, &quiet, &jsonWriter](const Json::Value& msg,
|
||||
const std::string& position,
|
||||
const bool /*verbose*/,
|
||||
std::atomic<bool>& /*throttled*/,
|
||||
std::atomic<bool> &
|
||||
/*fatalCobraError*/) -> bool {
|
||||
if (!quiet)
|
||||
{
|
||||
writeToStdout(fluentd, jsonWriter, msg, position);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
bool useQueue = false;
|
||||
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <ixcobra/IXCobraConfig.h>
|
||||
#include <string>
|
||||
#include <stddef.h>
|
||||
#include <cstdint>
|
||||
#include <ixcobra/IXCobraConfig.h>
|
||||
#include <stddef.h>
|
||||
#include <string>
|
||||
|
||||
namespace ix
|
||||
{
|
||||
|
||||
@@ -5,8 +5,9 @@
|
||||
*/
|
||||
|
||||
#include "IXQueueManager.h"
|
||||
#include <vector>
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
namespace ix
|
||||
{
|
||||
@@ -63,4 +64,4 @@ namespace ix
|
||||
_condition.notify_one();
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace ix
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <json/json.h>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <queue>
|
||||
#include <json/json.h>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <stddef.h>
|
||||
|
||||
namespace ix
|
||||
{
|
||||
@@ -32,4 +32,4 @@ namespace ix
|
||||
std::condition_variable _condition;
|
||||
size_t _maxQueueSize;
|
||||
};
|
||||
}
|
||||
} // namespace ix
|
||||
|
||||
@@ -39,24 +39,21 @@
|
||||
|
||||
#include "IXStatsdClient.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <ixwebsocket/IXNetSystem.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <iostream>
|
||||
|
||||
namespace ix
|
||||
{
|
||||
StatsdClient::StatsdClient(const std::string& host,
|
||||
int port,
|
||||
const std::string& prefix)
|
||||
: _host(host)
|
||||
, _port(port)
|
||||
, _prefix(prefix)
|
||||
, _stop(false)
|
||||
StatsdClient::StatsdClient(const std::string& host, int port, const std::string& prefix)
|
||||
: _host(host)
|
||||
, _port(port)
|
||||
, _prefix(prefix)
|
||||
, _stop(false)
|
||||
{
|
||||
_thread = std::thread([this]
|
||||
{
|
||||
_thread = std::thread([this] {
|
||||
while (!_stop)
|
||||
{
|
||||
flushQueue();
|
||||
@@ -119,8 +116,8 @@ namespace ix
|
||||
cleanup(key);
|
||||
|
||||
char buf[256];
|
||||
snprintf(buf, sizeof(buf), "%s%s:%zd|%s\n",
|
||||
_prefix.c_str(), key.c_str(), value, type.c_str());
|
||||
snprintf(
|
||||
buf, sizeof(buf), "%s%s:%zd|%s\n", _prefix.c_str(), key.c_str(), value, type.c_str());
|
||||
|
||||
enqueue(buf);
|
||||
return 0;
|
||||
@@ -142,9 +139,7 @@ namespace ix
|
||||
auto ret = _socket.sendto(message);
|
||||
if (ret != 0)
|
||||
{
|
||||
std::cerr << "error: "
|
||||
<< strerror(UdpSocket::getErrno())
|
||||
<< std::endl;
|
||||
std::cerr << "error: " << strerror(UdpSocket::getErrno()) << std::endl;
|
||||
}
|
||||
_queue.pop_front();
|
||||
}
|
||||
|
||||
@@ -6,21 +6,20 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <deque>
|
||||
#include <ixwebsocket/IXUdpSocket.h>
|
||||
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <deque>
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
|
||||
namespace ix
|
||||
{
|
||||
class StatsdClient
|
||||
{
|
||||
public:
|
||||
StatsdClient(const std::string& host="127.0.0.1",
|
||||
int port=8125,
|
||||
StatsdClient(const std::string& host = "127.0.0.1",
|
||||
int port = 8125,
|
||||
const std::string& prefix = "");
|
||||
~StatsdClient();
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ixwebsocket/IXWebSocketPerMessageDeflateOptions.h>
|
||||
#include <ixwebsocket/IXSocketTLSOptions.h>
|
||||
#include <ixwebsocket/IXWebSocketPerMessageDeflateOptions.h>
|
||||
|
||||
namespace ix
|
||||
{
|
||||
|
||||
@@ -5,17 +5,17 @@
|
||||
*/
|
||||
|
||||
#include "IXCobraConnection.h"
|
||||
#include <ixcrypto/IXHMac.h>
|
||||
#include <ixwebsocket/IXWebSocket.h>
|
||||
#include <ixwebsocket/IXSocketTLSOptions.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
#include <cmath>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <ixcrypto/IXHMac.h>
|
||||
#include <ixwebsocket/IXSocketTLSOptions.h>
|
||||
#include <ixwebsocket/IXWebSocket.h>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
|
||||
|
||||
namespace ix
|
||||
@@ -26,12 +26,12 @@ namespace ix
|
||||
constexpr CobraConnection::MsgId CobraConnection::kInvalidMsgId;
|
||||
constexpr int CobraConnection::kPingIntervalSecs;
|
||||
|
||||
CobraConnection::CobraConnection() :
|
||||
_webSocket(new WebSocket()),
|
||||
_publishMode(CobraConnection_PublishMode_Immediate),
|
||||
_authenticated(false),
|
||||
_eventCallback(nullptr),
|
||||
_id(1)
|
||||
CobraConnection::CobraConnection()
|
||||
: _webSocket(new WebSocket())
|
||||
, _publishMode(CobraConnection_PublishMode_Immediate)
|
||||
, _authenticated(false)
|
||||
, _eventCallback(nullptr)
|
||||
, _id(1)
|
||||
{
|
||||
_pdu["action"] = "rtm/publish";
|
||||
|
||||
@@ -97,11 +97,7 @@ namespace ix
|
||||
if (_eventCallback)
|
||||
{
|
||||
_eventCallback(
|
||||
std::make_unique<CobraEvent>(eventType,
|
||||
errorMsg,
|
||||
headers,
|
||||
subscriptionId,
|
||||
msgId));
|
||||
std::make_unique<CobraEvent>(eventType, errorMsg, headers, subscriptionId, msgId));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,126 +117,119 @@ namespace ix
|
||||
|
||||
void CobraConnection::initWebSocketOnMessageCallback()
|
||||
{
|
||||
_webSocket->setOnMessageCallback(
|
||||
[this](const ix::WebSocketMessagePtr& msg)
|
||||
_webSocket->setOnMessageCallback([this](const ix::WebSocketMessagePtr& msg) {
|
||||
CobraConnection::invokeTrafficTrackerCallback(msg->wireSize, true);
|
||||
|
||||
std::stringstream ss;
|
||||
if (msg->type == ix::WebSocketMessageType::Open)
|
||||
{
|
||||
CobraConnection::invokeTrafficTrackerCallback(msg->wireSize, true);
|
||||
invokeEventCallback(ix::CobraEventType::Open, std::string(), msg->openInfo.headers);
|
||||
sendHandshakeMessage();
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Close)
|
||||
{
|
||||
_authenticated = false;
|
||||
|
||||
std::stringstream ss;
|
||||
if (msg->type == ix::WebSocketMessageType::Open)
|
||||
ss << "Close code " << msg->closeInfo.code;
|
||||
ss << " reason " << msg->closeInfo.reason;
|
||||
invokeEventCallback(ix::CobraEventType::Closed, ss.str());
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Message)
|
||||
{
|
||||
Json::Value data;
|
||||
Json::Reader reader;
|
||||
if (!reader.parse(msg->str, data))
|
||||
{
|
||||
invokeEventCallback(ix::CobraEventType::Open,
|
||||
std::string(),
|
||||
msg->openInfo.headers);
|
||||
sendHandshakeMessage();
|
||||
invokeErrorCallback("Invalid json", msg->str);
|
||||
return;
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Close)
|
||||
{
|
||||
_authenticated = false;
|
||||
|
||||
std::stringstream ss;
|
||||
ss << "Close code " << msg->closeInfo.code;
|
||||
ss << " reason " << msg->closeInfo.reason;
|
||||
invokeEventCallback(ix::CobraEventType::Closed,
|
||||
ss.str());
|
||||
if (!data.isMember("action"))
|
||||
{
|
||||
invokeErrorCallback("Missing action", msg->str);
|
||||
return;
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Message)
|
||||
|
||||
auto action = data["action"].asString();
|
||||
|
||||
if (action == "auth/handshake/ok")
|
||||
{
|
||||
Json::Value data;
|
||||
Json::Reader reader;
|
||||
if (!reader.parse(msg->str, data))
|
||||
if (!handleHandshakeResponse(data))
|
||||
{
|
||||
invokeErrorCallback("Invalid json", msg->str);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!data.isMember("action"))
|
||||
{
|
||||
invokeErrorCallback("Missing action", msg->str);
|
||||
return;
|
||||
}
|
||||
|
||||
auto action = data["action"].asString();
|
||||
|
||||
if (action == "auth/handshake/ok")
|
||||
{
|
||||
if (!handleHandshakeResponse(data))
|
||||
{
|
||||
invokeErrorCallback("Error extracting nonce from handshake response", msg->str);
|
||||
}
|
||||
}
|
||||
else if (action == "auth/handshake/error")
|
||||
{
|
||||
invokeEventCallback(ix::CobraEventType::HandshakeError,
|
||||
invokeErrorCallback("Error extracting nonce from handshake response",
|
||||
msg->str);
|
||||
}
|
||||
else if (action == "auth/authenticate/ok")
|
||||
{
|
||||
_authenticated = true;
|
||||
invokeEventCallback(ix::CobraEventType::Authenticated);
|
||||
flushQueue();
|
||||
}
|
||||
else if (action == "auth/authenticate/error")
|
||||
{
|
||||
invokeEventCallback(ix::CobraEventType::AuthenticationError,
|
||||
msg->str);
|
||||
}
|
||||
else if (action == "rtm/subscription/data")
|
||||
{
|
||||
handleSubscriptionData(data);
|
||||
}
|
||||
else if (action == "rtm/subscribe/ok")
|
||||
{
|
||||
if (!handleSubscriptionResponse(data))
|
||||
{
|
||||
invokeErrorCallback("Error processing subscribe response", msg->str);
|
||||
}
|
||||
}
|
||||
else if (action == "rtm/subscribe/error")
|
||||
{
|
||||
invokeEventCallback(ix::CobraEventType::SubscriptionError,
|
||||
msg->str);
|
||||
}
|
||||
else if (action == "rtm/unsubscribe/ok")
|
||||
{
|
||||
if (!handleUnsubscriptionResponse(data))
|
||||
{
|
||||
invokeErrorCallback("Error processing unsubscribe response", msg->str);
|
||||
}
|
||||
}
|
||||
else if (action == "rtm/unsubscribe/error")
|
||||
{
|
||||
invokeErrorCallback("Unsubscription error", msg->str);
|
||||
}
|
||||
else if (action == "rtm/publish/ok")
|
||||
{
|
||||
if (!handlePublishResponse(data))
|
||||
{
|
||||
invokeErrorCallback("Error processing publish response", msg->str);
|
||||
}
|
||||
}
|
||||
else if (action == "rtm/publish/error")
|
||||
{
|
||||
invokeErrorCallback("Publish error", msg->str);
|
||||
}
|
||||
else
|
||||
{
|
||||
invokeErrorCallback("Un-handled message type", msg->str);
|
||||
}
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Error)
|
||||
else if (action == "auth/handshake/error")
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Connection error: " << msg->errorInfo.reason << std::endl;
|
||||
ss << "#retries: " << msg->errorInfo.retries << std::endl;
|
||||
ss << "Wait time(ms): " << msg->errorInfo.wait_time << std::endl;
|
||||
ss << "HTTP Status: " << msg->errorInfo.http_status << std::endl;
|
||||
invokeErrorCallback(ss.str(), std::string());
|
||||
invokeEventCallback(ix::CobraEventType::HandshakeError, msg->str);
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Pong)
|
||||
else if (action == "auth/authenticate/ok")
|
||||
{
|
||||
invokeEventCallback(ix::CobraEventType::Pong, msg->str);
|
||||
_authenticated = true;
|
||||
invokeEventCallback(ix::CobraEventType::Authenticated);
|
||||
flushQueue();
|
||||
}
|
||||
else if (action == "auth/authenticate/error")
|
||||
{
|
||||
invokeEventCallback(ix::CobraEventType::AuthenticationError, msg->str);
|
||||
}
|
||||
else if (action == "rtm/subscription/data")
|
||||
{
|
||||
handleSubscriptionData(data);
|
||||
}
|
||||
else if (action == "rtm/subscribe/ok")
|
||||
{
|
||||
if (!handleSubscriptionResponse(data))
|
||||
{
|
||||
invokeErrorCallback("Error processing subscribe response", msg->str);
|
||||
}
|
||||
}
|
||||
else if (action == "rtm/subscribe/error")
|
||||
{
|
||||
invokeEventCallback(ix::CobraEventType::SubscriptionError, msg->str);
|
||||
}
|
||||
else if (action == "rtm/unsubscribe/ok")
|
||||
{
|
||||
if (!handleUnsubscriptionResponse(data))
|
||||
{
|
||||
invokeErrorCallback("Error processing unsubscribe response", msg->str);
|
||||
}
|
||||
}
|
||||
else if (action == "rtm/unsubscribe/error")
|
||||
{
|
||||
invokeErrorCallback("Unsubscription error", msg->str);
|
||||
}
|
||||
else if (action == "rtm/publish/ok")
|
||||
{
|
||||
if (!handlePublishResponse(data))
|
||||
{
|
||||
invokeErrorCallback("Error processing publish response", msg->str);
|
||||
}
|
||||
}
|
||||
else if (action == "rtm/publish/error")
|
||||
{
|
||||
invokeErrorCallback("Publish error", msg->str);
|
||||
}
|
||||
else
|
||||
{
|
||||
invokeErrorCallback("Un-handled message type", msg->str);
|
||||
}
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Error)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Connection error: " << msg->errorInfo.reason << std::endl;
|
||||
ss << "#retries: " << msg->errorInfo.retries << std::endl;
|
||||
ss << "Wait time(ms): " << msg->errorInfo.wait_time << std::endl;
|
||||
ss << "HTTP Status: " << msg->errorInfo.http_status << std::endl;
|
||||
invokeErrorCallback(ss.str(), std::string());
|
||||
}
|
||||
else if (msg->type == ix::WebSocketMessageType::Pong)
|
||||
{
|
||||
invokeEventCallback(ix::CobraEventType::Pong, msg->str);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -254,12 +243,13 @@ namespace ix
|
||||
return _publishMode;
|
||||
}
|
||||
|
||||
void CobraConnection::configure(const std::string& appkey,
|
||||
const std::string& endpoint,
|
||||
const std::string& rolename,
|
||||
const std::string& rolesecret,
|
||||
const WebSocketPerMessageDeflateOptions& webSocketPerMessageDeflateOptions,
|
||||
const SocketTLSOptions& socketTLSOptions)
|
||||
void CobraConnection::configure(
|
||||
const std::string& appkey,
|
||||
const std::string& endpoint,
|
||||
const std::string& rolename,
|
||||
const std::string& rolesecret,
|
||||
const WebSocketPerMessageDeflateOptions& webSocketPerMessageDeflateOptions,
|
||||
const SocketTLSOptions& socketTLSOptions)
|
||||
{
|
||||
_roleName = rolename;
|
||||
_roleSecret = rolesecret;
|
||||
@@ -402,7 +392,8 @@ namespace ix
|
||||
if (!subscriptionId.isString()) return false;
|
||||
|
||||
invokeEventCallback(ix::CobraEventType::Subscribed,
|
||||
std::string(), WebSocketHttpHeaders(),
|
||||
std::string(),
|
||||
WebSocketHttpHeaders(),
|
||||
subscriptionId.asString());
|
||||
return true;
|
||||
}
|
||||
@@ -420,7 +411,8 @@ namespace ix
|
||||
if (!subscriptionId.isString()) return false;
|
||||
|
||||
invokeEventCallback(ix::CobraEventType::UnSubscribed,
|
||||
std::string(), WebSocketHttpHeaders(),
|
||||
std::string(),
|
||||
WebSocketHttpHeaders(),
|
||||
subscriptionId.asString());
|
||||
return true;
|
||||
}
|
||||
@@ -468,8 +460,10 @@ namespace ix
|
||||
uint64_t msgId = id.asUInt64();
|
||||
|
||||
invokeEventCallback(ix::CobraEventType::Published,
|
||||
std::string(), WebSocketHttpHeaders(),
|
||||
std::string(), msgId);
|
||||
std::string(),
|
||||
WebSocketHttpHeaders(),
|
||||
std::string(),
|
||||
msgId);
|
||||
|
||||
invokePublishTrackerCallback(false, true);
|
||||
|
||||
@@ -499,9 +493,7 @@ namespace ix
|
||||
}
|
||||
|
||||
std::pair<CobraConnection::MsgId, std::string> CobraConnection::prePublish(
|
||||
const Json::Value& channels,
|
||||
const Json::Value& msg,
|
||||
bool addToQueue)
|
||||
const Json::Value& channels, const Json::Value& msg, bool addToQueue)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_prePublishMutex);
|
||||
|
||||
@@ -667,8 +659,7 @@ namespace ix
|
||||
bool CobraConnection::publishMessage(const std::string& serializedJson)
|
||||
{
|
||||
auto webSocketSendInfo = _webSocket->send(serializedJson);
|
||||
CobraConnection::invokeTrafficTrackerCallback(webSocketSendInfo.wireSize,
|
||||
false);
|
||||
CobraConnection::invokeTrafficTrackerCallback(webSocketSendInfo.wireSize, false);
|
||||
return webSocketSendInfo.success;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,20 +6,19 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IXCobraConfig.h"
|
||||
#include "IXCobraEvent.h"
|
||||
#include "IXCobraEventType.h"
|
||||
#include <ixwebsocket/IXWebSocketHttpHeaders.h>
|
||||
#include <ixwebsocket/IXWebSocketPerMessageDeflateOptions.h>
|
||||
#include "IXCobraEventType.h"
|
||||
#include "IXCobraEvent.h"
|
||||
#include <json/json.h>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include <limits>
|
||||
|
||||
#include "IXCobraConfig.h"
|
||||
|
||||
#ifdef max
|
||||
#undef max
|
||||
@@ -121,10 +120,9 @@ namespace ix
|
||||
|
||||
/// Prepare a message for transmission
|
||||
/// (update the pdu, compute a msgId, serialize json to a string)
|
||||
std::pair<CobraConnection::MsgId, std::string> prePublish(
|
||||
const Json::Value& channels,
|
||||
const Json::Value& msg,
|
||||
bool addToQueue);
|
||||
std::pair<CobraConnection::MsgId, std::string> prePublish(const Json::Value& channels,
|
||||
const Json::Value& msg,
|
||||
bool addToQueue);
|
||||
|
||||
/// Attempt to send next message from the internal queue
|
||||
bool publishNext();
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "IXCobraEventType.h"
|
||||
#include <cstdint>
|
||||
#include <ixwebsocket/IXWebSocketHttpHeaders.h>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
|
||||
namespace ix
|
||||
{
|
||||
@@ -38,4 +38,4 @@ namespace ix
|
||||
};
|
||||
|
||||
using CobraEventPtr = std::unique_ptr<CobraEvent>;
|
||||
}
|
||||
} // namespace ix
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
*/
|
||||
|
||||
#include "IXCobraMetricsPublisher.h"
|
||||
#include <ixwebsocket/IXSocketTLSOptions.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <ixwebsocket/IXSocketTLSOptions.h>
|
||||
#include <stdexcept>
|
||||
|
||||
|
||||
@@ -17,8 +17,8 @@ namespace ix
|
||||
const std::string CobraMetricsPublisher::kSetRateControlId = "sms_set_rate_control_id";
|
||||
const std::string CobraMetricsPublisher::kSetBlacklistId = "sms_set_blacklist_id";
|
||||
|
||||
CobraMetricsPublisher::CobraMetricsPublisher() :
|
||||
_enabled(true)
|
||||
CobraMetricsPublisher::CobraMetricsPublisher()
|
||||
: _enabled(true)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -27,8 +27,7 @@ namespace ix
|
||||
;
|
||||
}
|
||||
|
||||
void CobraMetricsPublisher::configure(const CobraConfig& config,
|
||||
const std::string& channel)
|
||||
void CobraMetricsPublisher::configure(const CobraConfig& config, const std::string& channel)
|
||||
{
|
||||
// Configure the satori connection and start its publish background thread
|
||||
_cobra_metrics_theaded_publisher.configure(config, channel);
|
||||
@@ -42,7 +41,7 @@ namespace ix
|
||||
}
|
||||
|
||||
void CobraMetricsPublisher::setGenericAttributes(const std::string& attrName,
|
||||
const Json::Value& value)
|
||||
const Json::Value& value)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_device_mutex);
|
||||
_device[attrName] = value;
|
||||
@@ -107,8 +106,7 @@ namespace ix
|
||||
auto last_update = _last_update.find(id);
|
||||
if (last_update == _last_update.end()) return false;
|
||||
|
||||
auto timeDeltaFromLastSend =
|
||||
std::chrono::steady_clock::now() - last_update->second;
|
||||
auto timeDeltaFromLastSend = std::chrono::steady_clock::now() - last_update->second;
|
||||
|
||||
return timeDeltaFromLastSend < std::chrono::seconds(rate_control_it->second);
|
||||
}
|
||||
@@ -123,8 +121,7 @@ namespace ix
|
||||
{
|
||||
auto now = std::chrono::system_clock::now();
|
||||
auto ms =
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
now.time_since_epoch()).count();
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count();
|
||||
|
||||
return ms;
|
||||
}
|
||||
@@ -165,10 +162,9 @@ namespace ix
|
||||
return true;
|
||||
}
|
||||
|
||||
CobraConnection::MsgId CobraMetricsPublisher::push(
|
||||
const std::string& id,
|
||||
const Json::Value& data,
|
||||
bool shouldPushTest)
|
||||
CobraConnection::MsgId CobraMetricsPublisher::push(const std::string& id,
|
||||
const Json::Value& data,
|
||||
bool shouldPushTest)
|
||||
{
|
||||
if (shouldPushTest && !shouldPush(id)) return CobraConnection::kInvalidMsgId;
|
||||
|
||||
|
||||
@@ -40,8 +40,7 @@ namespace ix
|
||||
|
||||
/// Configuration / set keys, etc...
|
||||
/// All input data but the channel name is encrypted with rc4
|
||||
void configure(const CobraConfig& config,
|
||||
const std::string& channel);
|
||||
void configure(const CobraConfig& config, const std::string& channel);
|
||||
|
||||
/// Setter for the list of blacklisted metrics ids.
|
||||
/// That list is sorted internally for fast lookups
|
||||
@@ -68,10 +67,14 @@ namespace ix
|
||||
/// shouldPush method for places where we want to be as lightweight as possible when
|
||||
/// collecting metrics. When set to false, it is used so that we don't do double work when
|
||||
/// computing whether a metrics should be sent or not.
|
||||
CobraConnection::MsgId push(const std::string& id, const Json::Value& data, bool shouldPushTest = true);
|
||||
CobraConnection::MsgId push(const std::string& id,
|
||||
const Json::Value& data,
|
||||
bool shouldPushTest = true);
|
||||
|
||||
/// Interface used by lua. msg is a json encoded string.
|
||||
CobraConnection::MsgId push(const std::string& id, const std::string& data, bool shouldPushTest = true);
|
||||
CobraConnection::MsgId push(const std::string& id,
|
||||
const std::string& data,
|
||||
bool shouldPushTest = true);
|
||||
|
||||
/// Tells whether a metric can be pushed.
|
||||
/// A metric can be pushed if it satisfies those conditions:
|
||||
@@ -89,10 +92,16 @@ namespace ix
|
||||
void setGenericAttributes(const std::string& attrName, const Json::Value& value);
|
||||
|
||||
/// Set a unique id for the session. A uuid can be used.
|
||||
void setSession(const std::string& session) { _session = session; }
|
||||
void setSession(const std::string& session)
|
||||
{
|
||||
_session = session;
|
||||
}
|
||||
|
||||
/// Get the unique id used to identify the current session
|
||||
const std::string& getSession() const { return _session; }
|
||||
const std::string& getSession() const
|
||||
{
|
||||
return _session;
|
||||
}
|
||||
|
||||
/// Return the number of milliseconds since the epoch (~1970)
|
||||
uint64_t getMillisecondsSinceEpoch() const;
|
||||
|
||||
@@ -5,78 +5,77 @@
|
||||
*/
|
||||
|
||||
#include "IXCobraMetricsThreadedPublisher.h"
|
||||
#include <ixwebsocket/IXSetThreadName.h>
|
||||
#include <ixwebsocket/IXSocketTLSOptions.h>
|
||||
#include <ixcore/utils/IXCoreLogger.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
#include <cmath>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
#include <ixcore/utils/IXCoreLogger.h>
|
||||
#include <ixwebsocket/IXSetThreadName.h>
|
||||
#include <ixwebsocket/IXSocketTLSOptions.h>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
|
||||
|
||||
namespace ix
|
||||
{
|
||||
CobraMetricsThreadedPublisher::CobraMetricsThreadedPublisher() :
|
||||
_stop(false)
|
||||
CobraMetricsThreadedPublisher::CobraMetricsThreadedPublisher()
|
||||
: _stop(false)
|
||||
{
|
||||
_cobra_connection.setEventCallback([](const CobraEventPtr& event)
|
||||
_cobra_connection.setEventCallback([](const CobraEventPtr& event) {
|
||||
std::stringstream ss;
|
||||
|
||||
if (event->type == ix::CobraEventType::Open)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Handshake headers" << std::endl;
|
||||
|
||||
if (event->type == ix::CobraEventType::Open)
|
||||
for (auto&& it : event->headers)
|
||||
{
|
||||
ss << "Handshake headers" << std::endl;
|
||||
ss << it.first << ": " << it.second << std::endl;
|
||||
}
|
||||
}
|
||||
else if (event->type == ix::CobraEventType::Authenticated)
|
||||
{
|
||||
ss << "Authenticated";
|
||||
}
|
||||
else if (event->type == ix::CobraEventType::Error)
|
||||
{
|
||||
ss << "Error: " << event->errMsg;
|
||||
}
|
||||
else if (event->type == ix::CobraEventType::Closed)
|
||||
{
|
||||
ss << "Connection closed: " << event->errMsg;
|
||||
}
|
||||
else if (event->type == ix::CobraEventType::Subscribed)
|
||||
{
|
||||
ss << "Subscribed through subscription id: " << event->subscriptionId;
|
||||
}
|
||||
else if (event->type == ix::CobraEventType::UnSubscribed)
|
||||
{
|
||||
ss << "Unsubscribed through subscription id: " << event->subscriptionId;
|
||||
}
|
||||
else if (event->type == ix::CobraEventType::Published)
|
||||
{
|
||||
ss << "Published message " << event->msgId << " acked";
|
||||
}
|
||||
else if (event->type == ix::CobraEventType::Pong)
|
||||
{
|
||||
ss << "Received websocket pong";
|
||||
}
|
||||
else if (event->type == ix::CobraEventType::HandshakeError)
|
||||
{
|
||||
ss << "Handshake error: " << event->errMsg;
|
||||
}
|
||||
else if (event->type == ix::CobraEventType::AuthenticationError)
|
||||
{
|
||||
ss << "Authentication error: " << event->errMsg;
|
||||
}
|
||||
else if (event->type == ix::CobraEventType::SubscriptionError)
|
||||
{
|
||||
ss << "Subscription error: " << event->errMsg;
|
||||
}
|
||||
|
||||
for (auto&& it : event->headers)
|
||||
{
|
||||
ss << it.first << ": " << it.second << std::endl;
|
||||
}
|
||||
}
|
||||
else if (event->type == ix::CobraEventType::Authenticated)
|
||||
{
|
||||
ss << "Authenticated";
|
||||
}
|
||||
else if (event->type == ix::CobraEventType::Error)
|
||||
{
|
||||
ss << "Error: " << event->errMsg;
|
||||
}
|
||||
else if (event->type == ix::CobraEventType::Closed)
|
||||
{
|
||||
ss << "Connection closed: " << event->errMsg;
|
||||
}
|
||||
else if (event->type == ix::CobraEventType::Subscribed)
|
||||
{
|
||||
ss << "Subscribed through subscription id: " << event->subscriptionId;
|
||||
}
|
||||
else if (event->type == ix::CobraEventType::UnSubscribed)
|
||||
{
|
||||
ss << "Unsubscribed through subscription id: " << event->subscriptionId;
|
||||
}
|
||||
else if (event->type == ix::CobraEventType::Published)
|
||||
{
|
||||
ss << "Published message " << event->msgId << " acked";
|
||||
}
|
||||
else if (event->type == ix::CobraEventType::Pong)
|
||||
{
|
||||
ss << "Received websocket pong";
|
||||
}
|
||||
else if (event->type == ix::CobraEventType::HandshakeError)
|
||||
{
|
||||
ss << "Handshake error: " << event->errMsg;
|
||||
}
|
||||
else if (event->type == ix::CobraEventType::AuthenticationError)
|
||||
{
|
||||
ss << "Authentication error: " << event->errMsg;
|
||||
}
|
||||
else if (event->type == ix::CobraEventType::SubscriptionError)
|
||||
{
|
||||
ss << "Subscription error: " << event->errMsg;
|
||||
}
|
||||
|
||||
ix::IXCoreLogger::Log(ss.str().c_str());
|
||||
ix::IXCoreLogger::Log(ss.str().c_str());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -105,7 +104,6 @@ namespace ix
|
||||
|
||||
_channel = channel;
|
||||
_cobra_connection.configure(config);
|
||||
|
||||
}
|
||||
|
||||
void CobraMetricsThreadedPublisher::pushMessage(MessageKind messageKind)
|
||||
@@ -163,13 +161,15 @@ namespace ix
|
||||
{
|
||||
_cobra_connection.suspend();
|
||||
continue;
|
||||
}; break;
|
||||
};
|
||||
break;
|
||||
|
||||
case MessageKind::Resume:
|
||||
{
|
||||
_cobra_connection.resume();
|
||||
continue;
|
||||
}; break;
|
||||
};
|
||||
break;
|
||||
|
||||
case MessageKind::Message:
|
||||
{
|
||||
@@ -177,7 +177,8 @@ namespace ix
|
||||
{
|
||||
_cobra_connection.publishNext();
|
||||
}
|
||||
}; break;
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,8 +27,7 @@ namespace ix
|
||||
~CobraMetricsThreadedPublisher();
|
||||
|
||||
/// Configuration / set keys, etc...
|
||||
void configure(const CobraConfig& config,
|
||||
const std::string& channel);
|
||||
void configure(const CobraConfig& config, const std::string& channel);
|
||||
|
||||
/// Start the worker thread, used for background publishing
|
||||
void start();
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
|
||||
namespace ix
|
||||
{
|
||||
// Default do nothing logger
|
||||
IXCoreLogger::LogFunc IXCoreLogger::_currentLogger = [](const char* /*msg*/){};
|
||||
// Default do nothing logger
|
||||
IXCoreLogger::LogFunc IXCoreLogger::_currentLogger = [](const char* /*msg*/) {};
|
||||
|
||||
void IXCoreLogger::Log(const char* msg)
|
||||
{
|
||||
_currentLogger(msg);
|
||||
}
|
||||
void IXCoreLogger::Log(const char* msg)
|
||||
{
|
||||
_currentLogger(msg);
|
||||
}
|
||||
|
||||
} // ix
|
||||
} // namespace ix
|
||||
|
||||
@@ -9,7 +9,10 @@ namespace ix
|
||||
using LogFunc = std::function<void(const char*)>;
|
||||
static void Log(const char* msg);
|
||||
|
||||
static void setLogFunction(LogFunc& func) { _currentLogger = func; }
|
||||
static void setLogFunction(LogFunc& func)
|
||||
{
|
||||
_currentLogger = func;
|
||||
}
|
||||
|
||||
private:
|
||||
static LogFunc _currentLogger;
|
||||
|
||||
@@ -29,10 +29,9 @@
|
||||
|
||||
namespace ix
|
||||
{
|
||||
static const std::string base64_chars =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789+/";
|
||||
static const std::string base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789+/";
|
||||
|
||||
std::string base64_encode(const std::string& data, size_t len)
|
||||
{
|
||||
@@ -50,26 +49,26 @@ namespace ix
|
||||
unsigned char char_array_3[3];
|
||||
unsigned char char_array_4[4];
|
||||
|
||||
while(len--)
|
||||
while (len--)
|
||||
{
|
||||
char_array_3[i++] = *(bytes_to_encode++);
|
||||
if(i == 3)
|
||||
if (i == 3)
|
||||
{
|
||||
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
|
||||
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
|
||||
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
|
||||
char_array_4[3] = char_array_3[2] & 0x3f;
|
||||
|
||||
for(i = 0; (i <4) ; i++)
|
||||
for (i = 0; (i < 4); i++)
|
||||
ret += base64_chars[char_array_4[i]];
|
||||
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if(i)
|
||||
if (i)
|
||||
{
|
||||
for(j = i; j < 3; j++)
|
||||
for (j = i; j < 3; j++)
|
||||
char_array_3[j] = '\0';
|
||||
|
||||
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
|
||||
@@ -77,12 +76,11 @@ namespace ix
|
||||
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
|
||||
char_array_4[3] = char_array_3[2] & 0x3f;
|
||||
|
||||
for(j = 0; (j < i + 1); j++)
|
||||
for (j = 0; (j < i + 1); j++)
|
||||
ret += base64_chars[char_array_4[j]];
|
||||
|
||||
while((i++ < 3))
|
||||
while ((i++ < 3))
|
||||
ret += '=';
|
||||
|
||||
}
|
||||
|
||||
return ret;
|
||||
@@ -95,7 +93,7 @@ namespace ix
|
||||
|
||||
std::string base64_decode(const std::string& encoded_string)
|
||||
{
|
||||
int in_len = (int)encoded_string.size();
|
||||
int in_len = (int) encoded_string.size();
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
int in_ = 0;
|
||||
@@ -103,40 +101,42 @@ namespace ix
|
||||
std::string ret;
|
||||
ret.reserve(((in_len + 3) / 4) * 3);
|
||||
|
||||
while(in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_]))
|
||||
while (in_len-- && (encoded_string[in_] != '=') && is_base64(encoded_string[in_]))
|
||||
{
|
||||
char_array_4[i++] = encoded_string[in_]; in_++;
|
||||
if(i ==4)
|
||||
char_array_4[i++] = encoded_string[in_];
|
||||
in_++;
|
||||
if (i == 4)
|
||||
{
|
||||
for(i = 0; i <4; i++)
|
||||
for (i = 0; i < 4; i++)
|
||||
char_array_4[i] = base64_chars.find(char_array_4[i]);
|
||||
|
||||
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
|
||||
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
|
||||
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
|
||||
|
||||
for(i = 0; (i < 3); i++)
|
||||
for (i = 0; (i < 3); i++)
|
||||
ret += char_array_3[i];
|
||||
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if(i)
|
||||
if (i)
|
||||
{
|
||||
for(j = i; j <4; j++)
|
||||
for (j = i; j < 4; j++)
|
||||
char_array_4[j] = 0;
|
||||
|
||||
for(j = 0; j <4; j++)
|
||||
for (j = 0; j < 4; j++)
|
||||
char_array_4[j] = base64_chars.find(char_array_4[j]);
|
||||
|
||||
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
|
||||
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
|
||||
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
|
||||
|
||||
for(j = 0; (j < i - 1); j++) ret += char_array_3[j];
|
||||
for (j = 0; (j < i - 1); j++)
|
||||
ret += char_array_3[j];
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
} // namespace ix
|
||||
|
||||
@@ -5,16 +5,17 @@
|
||||
*/
|
||||
|
||||
#include "IXHMac.h"
|
||||
|
||||
#include "IXBase64.h"
|
||||
|
||||
#if defined(IXCRYPTO_USE_MBED_TLS)
|
||||
# include <mbedtls/md.h>
|
||||
#include <mbedtls/md.h>
|
||||
#elif defined(__APPLE__)
|
||||
# include <CommonCrypto/CommonHMAC.h>
|
||||
#include <CommonCrypto/CommonHMAC.h>
|
||||
#elif defined(IXCRYPTO_USE_OPEN_SSL)
|
||||
# include <openssl/hmac.h>
|
||||
#include <openssl/hmac.h>
|
||||
#else
|
||||
# include <assert.h>
|
||||
#include <assert.h>
|
||||
#endif
|
||||
|
||||
namespace ix
|
||||
@@ -26,19 +27,21 @@ namespace ix
|
||||
|
||||
#if defined(IXCRYPTO_USE_MBED_TLS)
|
||||
mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_MD5),
|
||||
(unsigned char *) key.c_str(), key.size(),
|
||||
(unsigned char *) data.c_str(), data.size(),
|
||||
(unsigned char *) &hash);
|
||||
(unsigned char*) key.c_str(),
|
||||
key.size(),
|
||||
(unsigned char*) data.c_str(),
|
||||
data.size(),
|
||||
(unsigned char*) &hash);
|
||||
#elif defined(__APPLE__)
|
||||
CCHmac(kCCHmacAlgMD5,
|
||||
key.c_str(), key.size(),
|
||||
data.c_str(), data.size(),
|
||||
&hash);
|
||||
CCHmac(kCCHmacAlgMD5, key.c_str(), key.size(), data.c_str(), data.size(), &hash);
|
||||
#elif defined(IXCRYPTO_USE_OPEN_SSL)
|
||||
HMAC(EVP_md5(),
|
||||
key.c_str(), (int) key.size(),
|
||||
(unsigned char *) data.c_str(), (int) data.size(),
|
||||
(unsigned char *) hash, nullptr);
|
||||
key.c_str(),
|
||||
(int) key.size(),
|
||||
(unsigned char*) data.c_str(),
|
||||
(int) data.size(),
|
||||
(unsigned char*) hash,
|
||||
nullptr);
|
||||
#else
|
||||
assert(false && "hmac not implemented on this platform");
|
||||
#endif
|
||||
@@ -47,4 +50,4 @@ namespace ix
|
||||
|
||||
return base64_encode(hashString, (uint32_t) hashString.size());
|
||||
}
|
||||
}
|
||||
} // namespace ix
|
||||
|
||||
@@ -19,4 +19,4 @@ namespace ix
|
||||
|
||||
return hashAddress;
|
||||
}
|
||||
}
|
||||
} // namespace ix
|
||||
|
||||
@@ -16,23 +16,23 @@
|
||||
|
||||
#include "IXUuid.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <iomanip>
|
||||
#include <random>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
|
||||
namespace ix
|
||||
{
|
||||
class Uuid
|
||||
{
|
||||
public:
|
||||
Uuid();
|
||||
std::string toString() const;
|
||||
public:
|
||||
Uuid();
|
||||
std::string toString() const;
|
||||
|
||||
private:
|
||||
uint64_t _ab;
|
||||
uint64_t _cd;
|
||||
private:
|
||||
uint64_t _ab;
|
||||
uint64_t _cd;
|
||||
};
|
||||
|
||||
Uuid::Uuid()
|
||||
@@ -60,7 +60,7 @@ namespace ix
|
||||
ss << std::setw(8) << (a);
|
||||
ss << std::setw(4) << (b >> 16);
|
||||
ss << std::setw(4) << (b & 0xFFFF);
|
||||
ss << std::setw(4) << (c >> 16 );
|
||||
ss << std::setw(4) << (c >> 16);
|
||||
ss << std::setw(4) << (c & 0xFFFF);
|
||||
ss << std::setw(8) << d;
|
||||
|
||||
@@ -72,4 +72,4 @@ namespace ix
|
||||
Uuid id;
|
||||
return id.toString();
|
||||
}
|
||||
}
|
||||
} // namespace ix
|
||||
|
||||
@@ -7,12 +7,12 @@
|
||||
#include "IXSentryClient.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <ixcore/utils/IXCoreLogger.h>
|
||||
#include <ixwebsocket/IXWebSocketHttpHeaders.h>
|
||||
#include <ixwebsocket/IXWebSocketVersion.h>
|
||||
#include <ixcore/utils/IXCoreLogger.h>
|
||||
#include <sstream>
|
||||
|
||||
|
||||
namespace ix
|
||||
@@ -246,24 +246,21 @@ namespace ix
|
||||
std::string SentryClient::computeUrl(const std::string& project, const std::string& key)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "https://sentry.io/api/"
|
||||
<< project
|
||||
<< "/minidump?sentry_key="
|
||||
<< key;
|
||||
ss << "https://sentry.io/api/" << project << "/minidump?sentry_key=" << key;
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
//
|
||||
// curl -v -X POST -F upload_file_minidump=@ws/crash.dmp 'https://sentry.io/api/123456/minidump?sentry_key=12344567890'
|
||||
// curl -v -X POST -F upload_file_minidump=@ws/crash.dmp
|
||||
// 'https://sentry.io/api/123456/minidump?sentry_key=12344567890'
|
||||
//
|
||||
void SentryClient::uploadMinidump(
|
||||
const std::string& sentryMetadata,
|
||||
const std::string& minidumpBytes,
|
||||
const std::string& project,
|
||||
const std::string& key,
|
||||
bool verbose,
|
||||
const OnResponseCallback& onResponseCallback)
|
||||
void SentryClient::uploadMinidump(const std::string& sentryMetadata,
|
||||
const std::string& minidumpBytes,
|
||||
const std::string& project,
|
||||
const std::string& key,
|
||||
bool verbose,
|
||||
const OnResponseCallback& onResponseCallback)
|
||||
{
|
||||
std::string multipartBoundary = _httpClient->generateMultipartBoundary();
|
||||
|
||||
@@ -283,15 +280,15 @@ namespace ix
|
||||
httpParameters["sentry"] = sentryMetadata;
|
||||
|
||||
args->url = computeUrl(project, key);
|
||||
args->body = _httpClient->serializeHttpFormDataParameters(multipartBoundary, httpFormDataParameters, httpParameters);
|
||||
args->body = _httpClient->serializeHttpFormDataParameters(
|
||||
multipartBoundary, httpFormDataParameters, httpParameters);
|
||||
|
||||
_httpClient->performRequest(args, onResponseCallback);
|
||||
}
|
||||
|
||||
void SentryClient::uploadPayload(
|
||||
const Json::Value& payload,
|
||||
bool verbose,
|
||||
const OnResponseCallback& onResponseCallback)
|
||||
void SentryClient::uploadPayload(const Json::Value& payload,
|
||||
bool verbose,
|
||||
const OnResponseCallback& onResponseCallback)
|
||||
{
|
||||
auto args = _httpClient->createRequest();
|
||||
args->extraHeaders["X-Sentry-Auth"] = SentryClient::computeAuthHeader();
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
#include <ixwebsocket/IXHttpClient.h>
|
||||
#include <ixwebsocket/IXSocketTLSOptions.h>
|
||||
#include <json/json.h>
|
||||
#include <regex>
|
||||
#include <memory>
|
||||
#include <regex>
|
||||
|
||||
namespace ix
|
||||
{
|
||||
@@ -28,18 +28,16 @@ namespace ix
|
||||
// Mostly for testing
|
||||
void setTLSOptions(const SocketTLSOptions& tlsOptions);
|
||||
|
||||
void uploadMinidump(
|
||||
const std::string& sentryMetadata,
|
||||
const std::string& minidumpBytes,
|
||||
const std::string& project,
|
||||
const std::string& key,
|
||||
bool verbose,
|
||||
const OnResponseCallback& onResponseCallback);
|
||||
void uploadMinidump(const std::string& sentryMetadata,
|
||||
const std::string& minidumpBytes,
|
||||
const std::string& project,
|
||||
const std::string& key,
|
||||
bool verbose,
|
||||
const OnResponseCallback& onResponseCallback);
|
||||
|
||||
void uploadPayload(
|
||||
const Json::Value& payload,
|
||||
bool verbose,
|
||||
const OnResponseCallback& onResponseCallback);
|
||||
void uploadPayload(const Json::Value& payload,
|
||||
bool verbose,
|
||||
const OnResponseCallback& onResponseCallback);
|
||||
|
||||
private:
|
||||
int64_t getTimestamp();
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ixwebsocket/IXSocketTLSOptions.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <ixwebsocket/IXSocketTLSOptions.h>
|
||||
|
||||
namespace snake
|
||||
{
|
||||
|
||||
@@ -28,10 +28,7 @@ namespace ix
|
||||
return false;
|
||||
}
|
||||
|
||||
CancellationRequest cancellationRequest = []() -> bool
|
||||
{
|
||||
return false;
|
||||
};
|
||||
CancellationRequest cancellationRequest = []() -> bool { return false; };
|
||||
|
||||
std::string errMsg;
|
||||
return _socket->connect(hostname, port, errMsg, cancellationRequest);
|
||||
@@ -252,9 +249,8 @@ namespace ix
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string RedisClient::prepareXaddCommand(
|
||||
const std::string& stream,
|
||||
const std::string& message)
|
||||
std::string RedisClient::prepareXaddCommand(const std::string& stream,
|
||||
const std::string& message)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "*5\r\n";
|
||||
@@ -328,7 +324,9 @@ namespace ix
|
||||
return streamId;
|
||||
}
|
||||
|
||||
bool RedisClient::sendCommand(const std::string& commands, int commandsCount, std::string& errMsg)
|
||||
bool RedisClient::sendCommand(const std::string& commands,
|
||||
int commandsCount,
|
||||
std::string& errMsg)
|
||||
{
|
||||
bool sent = _socket->writeBytes(commands, nullptr);
|
||||
if (!sent)
|
||||
|
||||
@@ -8,11 +8,10 @@
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <ixwebsocket/IXSocket.h>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <ixwebsocket/IXSocket.h>
|
||||
|
||||
namespace ix
|
||||
{
|
||||
class RedisClient
|
||||
@@ -39,14 +38,11 @@ namespace ix
|
||||
const OnRedisSubscribeCallback& callback);
|
||||
|
||||
// XADD
|
||||
std::string xadd(
|
||||
const std::string& channel,
|
||||
const std::string& message,
|
||||
std::string& errMsg);
|
||||
std::string xadd(const std::string& channel,
|
||||
const std::string& message,
|
||||
std::string& errMsg);
|
||||
|
||||
std::string prepareXaddCommand(
|
||||
const std::string& stream,
|
||||
const std::string& message);
|
||||
std::string prepareXaddCommand(const std::string& stream, const std::string& message);
|
||||
|
||||
std::string readXaddReply(std::string& errMsg);
|
||||
|
||||
|
||||
@@ -6,17 +6,18 @@
|
||||
|
||||
#include "IXRedisServer.h"
|
||||
|
||||
#include <ixwebsocket/IXNetSystem.h>
|
||||
#include <ixwebsocket/IXSocketConnect.h>
|
||||
#include <ixwebsocket/IXSocket.h>
|
||||
#include <ixwebsocket/IXCancellationRequest.h>
|
||||
#include <fstream>
|
||||
#include <ixwebsocket/IXCancellationRequest.h>
|
||||
#include <ixwebsocket/IXNetSystem.h>
|
||||
#include <ixwebsocket/IXSocket.h>
|
||||
#include <ixwebsocket/IXSocketConnect.h>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
namespace ix
|
||||
{
|
||||
RedisServer::RedisServer(int port, const std::string& host, int backlog, size_t maxConnections, int addressFamily)
|
||||
RedisServer::RedisServer(
|
||||
int port, const std::string& host, int backlog, size_t maxConnections, int addressFamily)
|
||||
: SocketServer(port, host, backlog, maxConnections, addressFamily)
|
||||
, _connectedClientsCount(0)
|
||||
, _stopHandlingConnections(false)
|
||||
@@ -114,8 +115,7 @@ namespace ix
|
||||
for (auto it : _subscribers)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Subscription id: " << it.first
|
||||
<< " #subscribers: " << it.second.size();
|
||||
ss << "Subscription id: " << it.first << " #subscribers: " << it.second.size();
|
||||
|
||||
logInfo(ss.str());
|
||||
}
|
||||
@@ -126,8 +126,7 @@ namespace ix
|
||||
return _connectedClientsCount;
|
||||
}
|
||||
|
||||
bool RedisServer::startsWith(const std::string& str,
|
||||
const std::string& start)
|
||||
bool RedisServer::startsWith(const std::string& str, const std::string& start)
|
||||
{
|
||||
return str.compare(0, start.length(), start) == 0;
|
||||
}
|
||||
@@ -144,9 +143,8 @@ namespace ix
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
bool RedisServer::parseRequest(
|
||||
std::unique_ptr<Socket>& socket,
|
||||
std::vector<std::string>& tokens)
|
||||
bool RedisServer::parseRequest(std::unique_ptr<Socket>& socket,
|
||||
std::vector<std::string>& tokens)
|
||||
{
|
||||
// Parse first line
|
||||
auto cb = makeCancellationRequestWithTimeout(30, _stopHandlingConnections);
|
||||
@@ -190,9 +188,8 @@ namespace ix
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RedisServer::handleCommand(
|
||||
std::unique_ptr<Socket>& socket,
|
||||
const std::vector<std::string>& tokens)
|
||||
bool RedisServer::handleCommand(std::unique_ptr<Socket>& socket,
|
||||
const std::vector<std::string>& tokens)
|
||||
{
|
||||
if (tokens.size() != 1) return false;
|
||||
|
||||
@@ -207,31 +204,30 @@ namespace ix
|
||||
//
|
||||
ss << "*6\r\n";
|
||||
ss << writeString("publish"); // 1
|
||||
ss << ":3\r\n"; // 2
|
||||
ss << "*0\r\n"; // 3
|
||||
ss << ":1\r\n"; // 4
|
||||
ss << ":2\r\n"; // 5
|
||||
ss << ":1\r\n"; // 6
|
||||
ss << ":3\r\n"; // 2
|
||||
ss << "*0\r\n"; // 3
|
||||
ss << ":1\r\n"; // 4
|
||||
ss << ":2\r\n"; // 5
|
||||
ss << ":1\r\n"; // 6
|
||||
|
||||
//
|
||||
// subscribe
|
||||
//
|
||||
ss << "*6\r\n";
|
||||
ss << writeString("subscribe"); // 1
|
||||
ss << ":2\r\n"; // 2
|
||||
ss << "*0\r\n"; // 3
|
||||
ss << ":1\r\n"; // 4
|
||||
ss << ":1\r\n"; // 5
|
||||
ss << ":1\r\n"; // 6
|
||||
ss << ":2\r\n"; // 2
|
||||
ss << "*0\r\n"; // 3
|
||||
ss << ":1\r\n"; // 4
|
||||
ss << ":1\r\n"; // 5
|
||||
ss << ":1\r\n"; // 6
|
||||
|
||||
socket->writeBytes(ss.str(), cb);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RedisServer::handleSubscribe(
|
||||
std::unique_ptr<Socket>& socket,
|
||||
const std::vector<std::string>& tokens)
|
||||
bool RedisServer::handleSubscribe(std::unique_ptr<Socket>& socket,
|
||||
const std::vector<std::string>& tokens)
|
||||
{
|
||||
if (tokens.size() != 2) return false;
|
||||
|
||||
@@ -250,9 +246,8 @@ namespace ix
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RedisServer::handlePublish(
|
||||
std::unique_ptr<Socket>& socket,
|
||||
const std::vector<std::string>& tokens)
|
||||
bool RedisServer::handlePublish(std::unique_ptr<Socket>& socket,
|
||||
const std::vector<std::string>& tokens)
|
||||
{
|
||||
if (tokens.size() != 3) return false;
|
||||
|
||||
@@ -281,9 +276,7 @@ namespace ix
|
||||
|
||||
// return the number of clients that received the message.
|
||||
std::stringstream ss;
|
||||
ss << ":"
|
||||
<< std::to_string(subscribers.size())
|
||||
<< "\r\n";
|
||||
ss << ":" << std::to_string(subscribers.size()) << "\r\n";
|
||||
socket->writeBytes(ss.str(), cb);
|
||||
|
||||
return true;
|
||||
|
||||
@@ -6,13 +6,13 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IXSocketServer.h"
|
||||
#include "IXSocket.h"
|
||||
#include "IXSocketServer.h"
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <utility> // pair
|
||||
@@ -50,18 +50,14 @@ namespace ix
|
||||
bool startsWith(const std::string& str, const std::string& start);
|
||||
std::string writeString(const std::string& str);
|
||||
|
||||
bool parseRequest(
|
||||
std::unique_ptr<Socket>& socket,
|
||||
std::vector<std::string>& tokens);
|
||||
bool parseRequest(std::unique_ptr<Socket>& socket, std::vector<std::string>& tokens);
|
||||
|
||||
bool handlePublish(std::unique_ptr<Socket>& socket,
|
||||
const std::vector<std::string>& tokens);
|
||||
bool handlePublish(std::unique_ptr<Socket>& socket, const std::vector<std::string>& tokens);
|
||||
|
||||
bool handleSubscribe(std::unique_ptr<Socket>& socket,
|
||||
const std::vector<std::string>& tokens);
|
||||
|
||||
bool handleCommand(std::unique_ptr<Socket>& socket,
|
||||
const std::vector<std::string>& tokens);
|
||||
bool handleCommand(std::unique_ptr<Socket>& socket, const std::vector<std::string>& tokens);
|
||||
|
||||
void cleanupSubscribers(std::unique_ptr<Socket>& socket);
|
||||
};
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
#include "IXSnakeConnectionState.h"
|
||||
#include "nlohmann/json.hpp"
|
||||
#include <iostream>
|
||||
#include <ixcore/utils/IXCoreLogger.h>
|
||||
#include <ixcrypto/IXHMac.h>
|
||||
#include <ixwebsocket/IXWebSocket.h>
|
||||
#include <ixcore/utils/IXCoreLogger.h>
|
||||
#include <sstream>
|
||||
|
||||
namespace snake
|
||||
@@ -189,7 +189,8 @@ namespace snake
|
||||
nlohmann::json response = {
|
||||
{"action", "rtm/subscription/data"},
|
||||
{"id", id++},
|
||||
{"body", {{"subscription_id", subscriptionId}, {"position", "0-0"}, {"messages", {msg}}}}};
|
||||
{"body",
|
||||
{{"subscription_id", subscriptionId}, {"position", "0-0"}, {"messages", {msg}}}}};
|
||||
|
||||
ws->sendText(response.dump());
|
||||
};
|
||||
@@ -261,8 +262,7 @@ namespace snake
|
||||
std::stringstream ss;
|
||||
ss << "malformed json pdu: " << e.what() << " -> " << str << "";
|
||||
|
||||
nlohmann::json response = {{"body", {{"error", "invalid_json"},
|
||||
{"reason", ss.str()}}}};
|
||||
nlohmann::json response = {{"body", {{"error", "invalid_json"}, {"reason", ss.str()}}}};
|
||||
ws->sendText(response.dump());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
#include "IXSnakeConnectionState.h"
|
||||
#include "IXSnakeProtocol.h"
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <ixcore/utils/IXCoreLogger.h>
|
||||
#include <sstream>
|
||||
|
||||
|
||||
namespace snake
|
||||
|
||||
@@ -31,11 +31,7 @@ TEST_CASE("dns", "[net]")
|
||||
auto dnsLookup = std::make_shared<DNSLookup>("wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww", 80);
|
||||
|
||||
std::string errMsg;
|
||||
struct addrinfo* res = dnsLookup->resolve(errMsg,
|
||||
[]
|
||||
{
|
||||
return false;
|
||||
});
|
||||
struct addrinfo* res = dnsLookup->resolve(errMsg, [] { return false; });
|
||||
std::cerr << "Error message: " << errMsg << std::endl;
|
||||
REQUIRE(res == nullptr);
|
||||
}
|
||||
@@ -46,11 +42,7 @@ TEST_CASE("dns", "[net]")
|
||||
|
||||
std::string errMsg;
|
||||
// The callback returning true means we are requesting cancellation
|
||||
struct addrinfo* res = dnsLookup->resolve(errMsg,
|
||||
[]
|
||||
{
|
||||
return true;
|
||||
});
|
||||
struct addrinfo* res = dnsLookup->resolve(errMsg, [] { return true; });
|
||||
std::cerr << "Error message: " << errMsg << std::endl;
|
||||
REQUIRE(res == nullptr);
|
||||
}
|
||||
|
||||
Vendored
+2
-4
@@ -1,4 +1,2 @@
|
||||
{
|
||||
"DisableFormat": true,
|
||||
"SortIncludes": false
|
||||
}
|
||||
DisableFormat: true
|
||||
SortIncludes: false
|
||||
|
||||
+1
-1
@@ -74,7 +74,7 @@ template<typename T>
|
||||
inline void pad3(T n, memory_buf_t &dest)
|
||||
{
|
||||
static_assert(std::is_unsigned<T>::value, "pad3 must get unsigned T");
|
||||
if(n < 1000)
|
||||
if (n < 1000)
|
||||
{
|
||||
dest.push_back(static_cast<char>(n / 100 + '0'));
|
||||
n = n % 100;
|
||||
|
||||
+1204
-906
File diff suppressed because it is too large
Load Diff
+513
-450
File diff suppressed because it is too large
Load Diff
+515
-434
File diff suppressed because it is too large
Load Diff
+1276
-969
File diff suppressed because it is too large
Load Diff
+1395
-1156
File diff suppressed because it is too large
Load Diff
+3369
-2577
File diff suppressed because it is too large
Load Diff
+35
-46
@@ -14,64 +14,53 @@
|
||||
FMT_BEGIN_NAMESPACE
|
||||
|
||||
namespace internal {
|
||||
template <typename Char>
|
||||
template<typename Char>
|
||||
typename buffer_context<Char>::iterator vformat_to(
|
||||
const std::locale& loc, buffer<Char>& buf,
|
||||
basic_string_view<Char> format_str,
|
||||
basic_format_args<buffer_context<Char>> args) {
|
||||
using range = buffer_range<Char>;
|
||||
return vformat_to<arg_formatter<range>>(buf, to_string_view(format_str), args,
|
||||
internal::locale_ref(loc));
|
||||
const std::locale &loc, buffer<Char> &buf, basic_string_view<Char> format_str, basic_format_args<buffer_context<Char>> args)
|
||||
{
|
||||
using range = buffer_range<Char>;
|
||||
return vformat_to<arg_formatter<range>>(buf, to_string_view(format_str), args, internal::locale_ref(loc));
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
std::basic_string<Char> vformat(const std::locale& loc,
|
||||
basic_string_view<Char> format_str,
|
||||
basic_format_args<buffer_context<Char>> args) {
|
||||
basic_memory_buffer<Char> buffer;
|
||||
internal::vformat_to(loc, buffer, format_str, args);
|
||||
return fmt::to_string(buffer);
|
||||
template<typename Char>
|
||||
std::basic_string<Char> vformat(const std::locale &loc, basic_string_view<Char> format_str, basic_format_args<buffer_context<Char>> args)
|
||||
{
|
||||
basic_memory_buffer<Char> buffer;
|
||||
internal::vformat_to(loc, buffer, format_str, args);
|
||||
return fmt::to_string(buffer);
|
||||
}
|
||||
} // namespace internal
|
||||
} // namespace internal
|
||||
|
||||
template <typename S, typename Char = char_t<S>>
|
||||
inline std::basic_string<Char> vformat(
|
||||
const std::locale& loc, const S& format_str,
|
||||
basic_format_args<buffer_context<Char>> args) {
|
||||
return internal::vformat(loc, to_string_view(format_str), args);
|
||||
template<typename S, typename Char = char_t<S>>
|
||||
inline std::basic_string<Char> vformat(const std::locale &loc, const S &format_str, basic_format_args<buffer_context<Char>> args)
|
||||
{
|
||||
return internal::vformat(loc, to_string_view(format_str), args);
|
||||
}
|
||||
|
||||
template <typename S, typename... Args, typename Char = char_t<S>>
|
||||
inline std::basic_string<Char> format(const std::locale& loc,
|
||||
const S& format_str, Args&&... args) {
|
||||
return internal::vformat(
|
||||
loc, to_string_view(format_str),
|
||||
{internal::make_args_checked<Args...>(format_str, args...)});
|
||||
template<typename S, typename... Args, typename Char = char_t<S>>
|
||||
inline std::basic_string<Char> format(const std::locale &loc, const S &format_str, Args &&... args)
|
||||
{
|
||||
return internal::vformat(loc, to_string_view(format_str), {internal::make_args_checked<Args...>(format_str, args...)});
|
||||
}
|
||||
|
||||
template <typename S, typename OutputIt, typename... Args,
|
||||
typename Char = enable_if_t<
|
||||
internal::is_output_iterator<OutputIt>::value, char_t<S>>>
|
||||
inline OutputIt vformat_to(OutputIt out, const std::locale& loc,
|
||||
const S& format_str,
|
||||
format_args_t<OutputIt, Char> args) {
|
||||
using range = internal::output_range<OutputIt, Char>;
|
||||
return vformat_to<arg_formatter<range>>(
|
||||
range(out), to_string_view(format_str), args, internal::locale_ref(loc));
|
||||
template<typename S, typename OutputIt, typename... Args,
|
||||
typename Char = enable_if_t<internal::is_output_iterator<OutputIt>::value, char_t<S>>>
|
||||
inline OutputIt vformat_to(OutputIt out, const std::locale &loc, const S &format_str, format_args_t<OutputIt, Char> args)
|
||||
{
|
||||
using range = internal::output_range<OutputIt, Char>;
|
||||
return vformat_to<arg_formatter<range>>(range(out), to_string_view(format_str), args, internal::locale_ref(loc));
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename S, typename... Args,
|
||||
FMT_ENABLE_IF(internal::is_output_iterator<OutputIt>::value&&
|
||||
internal::is_string<S>::value)>
|
||||
inline OutputIt format_to(OutputIt out, const std::locale& loc,
|
||||
const S& format_str, Args&&... args) {
|
||||
internal::check_format_string<Args...>(format_str);
|
||||
using context = format_context_t<OutputIt, char_t<S>>;
|
||||
format_arg_store<context, Args...> as{args...};
|
||||
return vformat_to(out, loc, to_string_view(format_str),
|
||||
basic_format_args<context>(as));
|
||||
template<typename OutputIt, typename S, typename... Args,
|
||||
FMT_ENABLE_IF(internal::is_output_iterator<OutputIt>::value &&internal::is_string<S>::value)>
|
||||
inline OutputIt format_to(OutputIt out, const std::locale &loc, const S &format_str, Args &&... args)
|
||||
{
|
||||
internal::check_format_string<Args...>(format_str);
|
||||
using context = format_context_t<OutputIt, char_t<S>>;
|
||||
format_arg_store<context, Args...> as{args...};
|
||||
return vformat_to(out, loc, to_string_view(format_str), basic_format_args<context>(as));
|
||||
}
|
||||
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_LOCALE_H_
|
||||
#endif // FMT_LOCALE_H_
|
||||
|
||||
+96
-86
@@ -14,111 +14,122 @@
|
||||
FMT_BEGIN_NAMESPACE
|
||||
namespace internal {
|
||||
|
||||
template <class Char> class formatbuf : public std::basic_streambuf<Char> {
|
||||
private:
|
||||
using int_type = typename std::basic_streambuf<Char>::int_type;
|
||||
using traits_type = typename std::basic_streambuf<Char>::traits_type;
|
||||
template<class Char>
|
||||
class formatbuf : public std::basic_streambuf<Char>
|
||||
{
|
||||
private:
|
||||
using int_type = typename std::basic_streambuf<Char>::int_type;
|
||||
using traits_type = typename std::basic_streambuf<Char>::traits_type;
|
||||
|
||||
buffer<Char>& buffer_;
|
||||
buffer<Char> &buffer_;
|
||||
|
||||
public:
|
||||
formatbuf(buffer<Char>& buf) : buffer_(buf) {}
|
||||
public:
|
||||
formatbuf(buffer<Char> &buf)
|
||||
: buffer_(buf)
|
||||
{}
|
||||
|
||||
protected:
|
||||
// The put-area is actually always empty. This makes the implementation
|
||||
// simpler and has the advantage that the streambuf and the buffer are always
|
||||
// in sync and sputc never writes into uninitialized memory. The obvious
|
||||
// disadvantage is that each call to sputc always results in a (virtual) call
|
||||
// to overflow. There is no disadvantage here for sputn since this always
|
||||
// results in a call to xsputn.
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
std::streamsize xsputn(const Char *s, std::streamsize count) FMT_OVERRIDE
|
||||
{
|
||||
buffer_.append(s, s + count);
|
||||
return count;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char> struct test_stream : std::basic_ostream<Char> {
|
||||
private:
|
||||
// Hide all operator<< from std::basic_ostream<Char>.
|
||||
void_t<> operator<<(null<>);
|
||||
void_t<> operator<<(const Char*);
|
||||
template<typename Char>
|
||||
struct test_stream : std::basic_ostream<Char>
|
||||
{
|
||||
private:
|
||||
// Hide all operator<< from std::basic_ostream<Char>.
|
||||
void_t<> operator<<(null<>);
|
||||
void_t<> operator<<(const Char *);
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(std::is_convertible<T, int>::value &&
|
||||
!std::is_enum<T>::value)>
|
||||
void_t<> operator<<(T);
|
||||
template<typename T, FMT_ENABLE_IF(std::is_convertible<T, int>::value && !std::is_enum<T>::value)>
|
||||
void_t<> operator<<(T);
|
||||
};
|
||||
|
||||
// Checks if T has a user-defined operator<< (e.g. not a member of
|
||||
// std::ostream).
|
||||
template <typename T, typename Char> class is_streamable {
|
||||
private:
|
||||
template <typename U>
|
||||
static bool_constant<!std::is_same<decltype(std::declval<test_stream<Char>&>()
|
||||
<< std::declval<U>()),
|
||||
void_t<>>::value>
|
||||
test(int);
|
||||
template<typename T, typename Char>
|
||||
class is_streamable
|
||||
{
|
||||
private:
|
||||
template<typename U>
|
||||
static bool_constant<!std::is_same<decltype(std::declval<test_stream<Char> &>() << std::declval<U>()), void_t<>>::value> test(int);
|
||||
|
||||
template <typename> static std::false_type test(...);
|
||||
template<typename>
|
||||
static std::false_type test(...);
|
||||
|
||||
using result = decltype(test<T>(0));
|
||||
using result = decltype(test<T>(0));
|
||||
|
||||
public:
|
||||
static const bool value = result::value;
|
||||
public:
|
||||
static const bool value = result::value;
|
||||
};
|
||||
|
||||
// Write the content of buf to os.
|
||||
template <typename Char>
|
||||
void write(std::basic_ostream<Char>& os, buffer<Char>& buf) {
|
||||
const Char* buf_data = buf.data();
|
||||
using unsigned_streamsize = std::make_unsigned<std::streamsize>::type;
|
||||
unsigned_streamsize size = buf.size();
|
||||
unsigned_streamsize max_size = to_unsigned(max_value<std::streamsize>());
|
||||
do {
|
||||
unsigned_streamsize n = size <= max_size ? size : max_size;
|
||||
os.write(buf_data, static_cast<std::streamsize>(n));
|
||||
buf_data += n;
|
||||
size -= n;
|
||||
} while (size != 0);
|
||||
template<typename Char>
|
||||
void write(std::basic_ostream<Char> &os, buffer<Char> &buf)
|
||||
{
|
||||
const Char *buf_data = buf.data();
|
||||
using unsigned_streamsize = std::make_unsigned<std::streamsize>::type;
|
||||
unsigned_streamsize size = buf.size();
|
||||
unsigned_streamsize max_size = to_unsigned(max_value<std::streamsize>());
|
||||
do
|
||||
{
|
||||
unsigned_streamsize n = size <= max_size ? size : max_size;
|
||||
os.write(buf_data, static_cast<std::streamsize>(n));
|
||||
buf_data += n;
|
||||
size -= n;
|
||||
} while (size != 0);
|
||||
}
|
||||
|
||||
template <typename Char, typename T>
|
||||
void format_value(buffer<Char>& buf, const T& value,
|
||||
locale_ref loc = locale_ref()) {
|
||||
formatbuf<Char> format_buf(buf);
|
||||
std::basic_ostream<Char> output(&format_buf);
|
||||
if (loc) output.imbue(loc.get<std::locale>());
|
||||
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
|
||||
output << value;
|
||||
buf.resize(buf.size());
|
||||
template<typename Char, typename T>
|
||||
void format_value(buffer<Char> &buf, const T &value, locale_ref loc = locale_ref())
|
||||
{
|
||||
formatbuf<Char> format_buf(buf);
|
||||
std::basic_ostream<Char> output(&format_buf);
|
||||
if (loc)
|
||||
output.imbue(loc.get<std::locale>());
|
||||
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
|
||||
output << value;
|
||||
buf.resize(buf.size());
|
||||
}
|
||||
|
||||
// Formats an object of type T that has an overloaded ostream operator<<.
|
||||
template <typename T, typename Char>
|
||||
struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
|
||||
: formatter<basic_string_view<Char>, Char> {
|
||||
template <typename Context>
|
||||
auto format(const T& value, Context& ctx) -> decltype(ctx.out()) {
|
||||
basic_memory_buffer<Char> buffer;
|
||||
format_value(buffer, value, ctx.locale());
|
||||
basic_string_view<Char> str(buffer.data(), buffer.size());
|
||||
return formatter<basic_string_view<Char>, Char>::format(str, ctx);
|
||||
}
|
||||
template<typename T, typename Char>
|
||||
struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>> : formatter<basic_string_view<Char>, Char>
|
||||
{
|
||||
template<typename Context>
|
||||
auto format(const T &value, Context &ctx) -> decltype(ctx.out())
|
||||
{
|
||||
basic_memory_buffer<Char> buffer;
|
||||
format_value(buffer, value, ctx.locale());
|
||||
basic_string_view<Char> str(buffer.data(), buffer.size());
|
||||
return formatter<basic_string_view<Char>, Char>::format(str, ctx);
|
||||
}
|
||||
};
|
||||
} // namespace internal
|
||||
} // namespace internal
|
||||
|
||||
template <typename Char>
|
||||
void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
|
||||
basic_format_args<buffer_context<Char>> args) {
|
||||
basic_memory_buffer<Char> buffer;
|
||||
internal::vformat_to(buffer, format_str, args);
|
||||
internal::write(os, buffer);
|
||||
template<typename Char>
|
||||
void vprint(std::basic_ostream<Char> &os, basic_string_view<Char> format_str, basic_format_args<buffer_context<Char>> args)
|
||||
{
|
||||
basic_memory_buffer<Char> buffer;
|
||||
internal::vformat_to(buffer, format_str, args);
|
||||
internal::write(os, buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -130,12 +141,11 @@ void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
|
||||
fmt::print(cerr, "Don't {}!", "panic");
|
||||
\endrst
|
||||
*/
|
||||
template <typename S, typename... Args,
|
||||
typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
|
||||
void print(std::basic_ostream<Char>& os, const S& format_str, Args&&... args) {
|
||||
vprint(os, to_string_view(format_str),
|
||||
{internal::make_args_checked<Args...>(format_str, args...)});
|
||||
template<typename S, typename... Args, typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
|
||||
void print(std::basic_ostream<Char> &os, const S &format_str, Args &&... args)
|
||||
{
|
||||
vprint(os, to_string_view(format_str), {internal::make_args_checked<Args...>(format_str, args...)});
|
||||
}
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_OSTREAM_H_
|
||||
#endif // FMT_OSTREAM_H_
|
||||
|
||||
+225
-172
@@ -10,65 +10,65 @@
|
||||
|
||||
#if defined(__MINGW32__) || defined(__CYGWIN__)
|
||||
// Workaround MinGW bug https://sourceforge.net/p/mingw/bugs/2024/.
|
||||
# undef __STRICT_ANSI__
|
||||
#undef __STRICT_ANSI__
|
||||
#endif
|
||||
|
||||
#include <cerrno>
|
||||
#include <clocale> // for locale_t
|
||||
#include <clocale> // for locale_t
|
||||
#include <cstdio>
|
||||
#include <cstdlib> // for strtod_l
|
||||
#include <cstdlib> // for strtod_l
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#if defined __APPLE__ || defined(__FreeBSD__)
|
||||
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X
|
||||
#include <xlocale.h> // for LC_NUMERIC_MASK on OS X
|
||||
#endif
|
||||
|
||||
#include "format.h"
|
||||
|
||||
// UWP doesn't provide _pipe.
|
||||
#if FMT_HAS_INCLUDE("winapifamily.h")
|
||||
# include <winapifamily.h>
|
||||
#include <winapifamily.h>
|
||||
#endif
|
||||
#if FMT_HAS_INCLUDE("fcntl.h") && \
|
||||
(!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
|
||||
# include <fcntl.h> // for O_RDONLY
|
||||
# define FMT_USE_FCNTL 1
|
||||
#if FMT_HAS_INCLUDE("fcntl.h") && (!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
|
||||
#include <fcntl.h> // for O_RDONLY
|
||||
#define FMT_USE_FCNTL 1
|
||||
#else
|
||||
# define FMT_USE_FCNTL 0
|
||||
#define FMT_USE_FCNTL 0
|
||||
#endif
|
||||
|
||||
#ifndef FMT_POSIX
|
||||
# if defined(_WIN32) && !defined(__MINGW32__)
|
||||
#if defined(_WIN32) && !defined(__MINGW32__)
|
||||
// Fix warnings about deprecated symbols.
|
||||
# define FMT_POSIX(call) _##call
|
||||
# else
|
||||
# define FMT_POSIX(call) call
|
||||
# endif
|
||||
#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)
|
||||
#define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
|
||||
#else
|
||||
# define FMT_SYSTEM(call) call
|
||||
# ifdef _WIN32
|
||||
#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
|
||||
#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); \
|
||||
#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)
|
||||
#define FMT_RETRY_VAL(result, expression, error_result) result = (expression)
|
||||
#endif
|
||||
|
||||
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
|
||||
@@ -100,91 +100,114 @@ FMT_BEGIN_NAMESPACE
|
||||
format(std::string("{}"), 42);
|
||||
\endrst
|
||||
*/
|
||||
template <typename Char> class basic_cstring_view {
|
||||
private:
|
||||
const Char* data_;
|
||||
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) {}
|
||||
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()) {}
|
||||
/**
|
||||
\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_; }
|
||||
/** Returns the pointer to a C string. */
|
||||
const Char *c_str() const
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
};
|
||||
|
||||
using cstring_view = basic_cstring_view<char>;
|
||||
using wcstring_view = basic_cstring_view<wchar_t>;
|
||||
|
||||
// An error code.
|
||||
class error_code {
|
||||
private:
|
||||
int value_;
|
||||
class error_code
|
||||
{
|
||||
private:
|
||||
int value_;
|
||||
|
||||
public:
|
||||
explicit error_code(int value = 0) FMT_NOEXCEPT : value_(value) {}
|
||||
public:
|
||||
explicit error_code(int value = 0) FMT_NOEXCEPT : value_(value) {}
|
||||
|
||||
int get() const FMT_NOEXCEPT { return value_; }
|
||||
int get() const FMT_NOEXCEPT
|
||||
{
|
||||
return value_;
|
||||
}
|
||||
};
|
||||
|
||||
// A buffered file.
|
||||
class buffered_file {
|
||||
private:
|
||||
FILE* file_;
|
||||
class buffered_file
|
||||
{
|
||||
private:
|
||||
FILE *file_;
|
||||
|
||||
friend class file;
|
||||
friend class file;
|
||||
|
||||
explicit buffered_file(FILE* f) : file_(f) {}
|
||||
explicit buffered_file(FILE *f)
|
||||
: file_(f)
|
||||
{}
|
||||
|
||||
public:
|
||||
buffered_file(const buffered_file&) = delete;
|
||||
void operator=(const buffered_file&) = delete;
|
||||
public:
|
||||
buffered_file(const buffered_file &) = delete;
|
||||
void operator=(const buffered_file &) = delete;
|
||||
|
||||
// Constructs a buffered_file object which doesn't represent any file.
|
||||
buffered_file() FMT_NOEXCEPT : file_(nullptr) {}
|
||||
// Constructs a buffered_file object which doesn't represent any file.
|
||||
buffered_file() FMT_NOEXCEPT : file_(nullptr) {}
|
||||
|
||||
// Destroys the object closing the file it represents if any.
|
||||
FMT_API ~buffered_file() FMT_NOEXCEPT;
|
||||
// Destroys the object closing the file it represents if any.
|
||||
FMT_API ~buffered_file() FMT_NOEXCEPT;
|
||||
|
||||
public:
|
||||
buffered_file(buffered_file&& other) FMT_NOEXCEPT : file_(other.file_) {
|
||||
other.file_ = nullptr;
|
||||
}
|
||||
public:
|
||||
buffered_file(buffered_file &&other) FMT_NOEXCEPT : file_(other.file_)
|
||||
{
|
||||
other.file_ = nullptr;
|
||||
}
|
||||
|
||||
buffered_file& operator=(buffered_file&& other) {
|
||||
close();
|
||||
file_ = other.file_;
|
||||
other.file_ = nullptr;
|
||||
return *this;
|
||||
}
|
||||
buffered_file &operator=(buffered_file &&other)
|
||||
{
|
||||
close();
|
||||
file_ = other.file_;
|
||||
other.file_ = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Opens a file.
|
||||
FMT_API buffered_file(cstring_view filename, cstring_view mode);
|
||||
// Opens a file.
|
||||
FMT_API buffered_file(cstring_view filename, cstring_view mode);
|
||||
|
||||
// Closes the file.
|
||||
FMT_API void close();
|
||||
// Closes the file.
|
||||
FMT_API void close();
|
||||
|
||||
// Returns the pointer to a FILE object representing this file.
|
||||
FILE* get() const FMT_NOEXCEPT { return file_; }
|
||||
// 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;
|
||||
// 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);
|
||||
}
|
||||
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...));
|
||||
}
|
||||
template<typename... Args>
|
||||
inline void print(string_view format_str, const Args &... args)
|
||||
{
|
||||
vprint(format_str, make_format_args(args...));
|
||||
}
|
||||
};
|
||||
|
||||
#if FMT_USE_FCNTL
|
||||
@@ -194,128 +217,158 @@ class buffered_file {
|
||||
// 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.
|
||||
class file
|
||||
{
|
||||
private:
|
||||
int fd_; // File descriptor.
|
||||
|
||||
// Constructs a file object with a given descriptor.
|
||||
explicit file(int fd) : fd_(fd) {}
|
||||
// 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.
|
||||
};
|
||||
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) {}
|
||||
// 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);
|
||||
// Opens a file and constructs a file object representing this file.
|
||||
FMT_API file(cstring_view path, int oflag);
|
||||
|
||||
public:
|
||||
file(const file&) = delete;
|
||||
void operator=(const file&) = delete;
|
||||
public:
|
||||
file(const file &) = delete;
|
||||
void operator=(const file &) = delete;
|
||||
|
||||
file(file&& other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; }
|
||||
file(file &&other) FMT_NOEXCEPT : fd_(other.fd_)
|
||||
{
|
||||
other.fd_ = -1;
|
||||
}
|
||||
|
||||
file& operator=(file&& other) FMT_NOEXCEPT {
|
||||
close();
|
||||
fd_ = other.fd_;
|
||||
other.fd_ = -1;
|
||||
return *this;
|
||||
}
|
||||
file &operator=(file &&other) FMT_NOEXCEPT
|
||||
{
|
||||
close();
|
||||
fd_ = other.fd_;
|
||||
other.fd_ = -1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Destroys the object closing the file it represents if any.
|
||||
FMT_API ~file() FMT_NOEXCEPT;
|
||||
// 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_; }
|
||||
// Returns the file descriptor.
|
||||
int descriptor() const FMT_NOEXCEPT
|
||||
{
|
||||
return fd_;
|
||||
}
|
||||
|
||||
// Closes the file.
|
||||
FMT_API void close();
|
||||
// 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;
|
||||
// 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 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);
|
||||
// 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);
|
||||
// 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);
|
||||
|
||||
// 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;
|
||||
// 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 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);
|
||||
// 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();
|
||||
#endif // FMT_USE_FCNTL
|
||||
#endif // FMT_USE_FCNTL
|
||||
|
||||
#ifdef FMT_LOCALE
|
||||
// A "C" numeric locale.
|
||||
class Locale {
|
||||
private:
|
||||
# ifdef _WIN32
|
||||
using locale_t = _locale_t;
|
||||
class Locale
|
||||
{
|
||||
private:
|
||||
#ifdef _WIN32
|
||||
using locale_t = _locale_t;
|
||||
|
||||
enum { LC_NUMERIC_MASK = LC_NUMERIC };
|
||||
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 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 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
|
||||
static double strtod_l(const char *nptr, char **endptr, _locale_t locale)
|
||||
{
|
||||
return _strtod_l(nptr, endptr, locale);
|
||||
}
|
||||
#endif
|
||||
|
||||
locale_t locale_;
|
||||
locale_t locale_;
|
||||
|
||||
public:
|
||||
using type = locale_t;
|
||||
Locale(const Locale&) = delete;
|
||||
void operator=(const Locale&) = delete;
|
||||
public:
|
||||
using type = locale_t;
|
||||
Locale(const Locale &) = delete;
|
||||
void operator=(const Locale &) = delete;
|
||||
|
||||
Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", nullptr)) {
|
||||
if (!locale_) FMT_THROW(system_error(errno, "cannot create locale"));
|
||||
}
|
||||
~Locale() { freelocale(locale_); }
|
||||
Locale()
|
||||
: locale_(newlocale(LC_NUMERIC_MASK, "C", nullptr))
|
||||
{
|
||||
if (!locale_)
|
||||
FMT_THROW(system_error(errno, "cannot create locale"));
|
||||
}
|
||||
~Locale()
|
||||
{
|
||||
freelocale(locale_);
|
||||
}
|
||||
|
||||
type get() const { return 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 = nullptr;
|
||||
double result = strtod_l(str, &end, locale_);
|
||||
str = end;
|
||||
return result;
|
||||
}
|
||||
// Converts string to floating-point number and advances str past the end
|
||||
// of the parsed input.
|
||||
double strtod(const char *&str) const
|
||||
{
|
||||
char *end = nullptr;
|
||||
double result = strtod_l(str, &end, locale_);
|
||||
str = end;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
#endif // FMT_LOCALE
|
||||
#endif // FMT_LOCALE
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_POSIX_H_
|
||||
#endif // FMT_POSIX_H_
|
||||
|
||||
+638
-520
File diff suppressed because it is too large
Load Diff
+298
-248
@@ -17,324 +17,374 @@
|
||||
|
||||
// output only up to N items from the range.
|
||||
#ifndef FMT_RANGE_OUTPUT_LENGTH_LIMIT
|
||||
# define FMT_RANGE_OUTPUT_LENGTH_LIMIT 256
|
||||
#define FMT_RANGE_OUTPUT_LENGTH_LIMIT 256
|
||||
#endif
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
|
||||
template <typename Char> struct formatting_base {
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
return ctx.begin();
|
||||
}
|
||||
template<typename Char>
|
||||
struct formatting_base
|
||||
{
|
||||
template<typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin())
|
||||
{
|
||||
return ctx.begin();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char, typename Enable = void>
|
||||
struct formatting_range : formatting_base<Char> {
|
||||
static FMT_CONSTEXPR_DECL const std::size_t range_length_limit =
|
||||
FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the
|
||||
// range.
|
||||
Char prefix;
|
||||
Char delimiter;
|
||||
Char postfix;
|
||||
formatting_range() : prefix('{'), delimiter(','), postfix('}') {}
|
||||
static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true;
|
||||
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
|
||||
template<typename Char, typename Enable = void>
|
||||
struct formatting_range : formatting_base<Char>
|
||||
{
|
||||
static FMT_CONSTEXPR_DECL const std::size_t range_length_limit = FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the
|
||||
// range.
|
||||
Char prefix;
|
||||
Char delimiter;
|
||||
Char postfix;
|
||||
formatting_range()
|
||||
: prefix('{')
|
||||
, delimiter(',')
|
||||
, postfix('}')
|
||||
{}
|
||||
static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true;
|
||||
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
|
||||
};
|
||||
|
||||
template <typename Char, typename Enable = void>
|
||||
struct formatting_tuple : formatting_base<Char> {
|
||||
Char prefix;
|
||||
Char delimiter;
|
||||
Char postfix;
|
||||
formatting_tuple() : prefix('('), delimiter(','), postfix(')') {}
|
||||
static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true;
|
||||
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
|
||||
template<typename Char, typename Enable = void>
|
||||
struct formatting_tuple : formatting_base<Char>
|
||||
{
|
||||
Char prefix;
|
||||
Char delimiter;
|
||||
Char postfix;
|
||||
formatting_tuple()
|
||||
: prefix('(')
|
||||
, delimiter(',')
|
||||
, postfix(')')
|
||||
{}
|
||||
static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true;
|
||||
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
|
||||
};
|
||||
|
||||
namespace internal {
|
||||
|
||||
template <typename RangeT, typename OutputIterator>
|
||||
OutputIterator copy(const RangeT& range, OutputIterator out) {
|
||||
for (auto it = range.begin(), end = range.end(); it != end; ++it)
|
||||
*out++ = *it;
|
||||
return out;
|
||||
template<typename RangeT, typename OutputIterator>
|
||||
OutputIterator copy(const RangeT &range, OutputIterator out)
|
||||
{
|
||||
for (auto it = range.begin(), end = range.end(); it != end; ++it)
|
||||
*out++ = *it;
|
||||
return out;
|
||||
}
|
||||
|
||||
template <typename OutputIterator>
|
||||
OutputIterator copy(const char* str, OutputIterator out) {
|
||||
while (*str) *out++ = *str++;
|
||||
return out;
|
||||
template<typename OutputIterator>
|
||||
OutputIterator copy(const char *str, OutputIterator out)
|
||||
{
|
||||
while (*str)
|
||||
*out++ = *str++;
|
||||
return out;
|
||||
}
|
||||
|
||||
template <typename OutputIterator>
|
||||
OutputIterator copy(char ch, OutputIterator out) {
|
||||
*out++ = ch;
|
||||
return out;
|
||||
template<typename OutputIterator>
|
||||
OutputIterator copy(char ch, OutputIterator out)
|
||||
{
|
||||
*out++ = ch;
|
||||
return out;
|
||||
}
|
||||
|
||||
/// Return true value if T has std::string interface, like std::string_view.
|
||||
template <typename T> class is_like_std_string {
|
||||
template <typename U>
|
||||
static auto check(U* p)
|
||||
-> decltype((void)p->find('a'), p->length(), (void)p->data(), int());
|
||||
template <typename> static void check(...);
|
||||
template<typename T>
|
||||
class is_like_std_string
|
||||
{
|
||||
template<typename U>
|
||||
static auto check(U *p) -> decltype((void)p->find('a'), p->length(), (void)p->data(), int());
|
||||
template<typename>
|
||||
static void check(...);
|
||||
|
||||
public:
|
||||
static FMT_CONSTEXPR_DECL const bool value =
|
||||
is_string<T>::value || !std::is_void<decltype(check<T>(nullptr))>::value;
|
||||
public:
|
||||
static FMT_CONSTEXPR_DECL const bool value = is_string<T>::value || !std::is_void<decltype(check<T>(nullptr))>::value;
|
||||
};
|
||||
|
||||
template <typename Char>
|
||||
struct is_like_std_string<fmt::basic_string_view<Char>> : std::true_type {};
|
||||
template<typename Char>
|
||||
struct is_like_std_string<fmt::basic_string_view<Char>> : std::true_type
|
||||
{};
|
||||
|
||||
template <typename... Ts> struct conditional_helper {};
|
||||
template<typename... Ts>
|
||||
struct conditional_helper
|
||||
{};
|
||||
|
||||
template <typename T, typename _ = void> struct is_range_ : std::false_type {};
|
||||
template<typename T, typename _ = void>
|
||||
struct is_range_ : std::false_type
|
||||
{};
|
||||
|
||||
#if !FMT_MSC_VER || FMT_MSC_VER > 1800
|
||||
template <typename T>
|
||||
struct is_range_<
|
||||
T, conditional_t<false,
|
||||
conditional_helper<decltype(std::declval<T>().begin()),
|
||||
decltype(std::declval<T>().end())>,
|
||||
void>> : std::true_type {};
|
||||
template<typename T>
|
||||
struct is_range_<T, conditional_t<false, conditional_helper<decltype(std::declval<T>().begin()), decltype(std::declval<T>().end())>, void>>
|
||||
: std::true_type
|
||||
{};
|
||||
#endif
|
||||
|
||||
/// tuple_size and tuple_element check.
|
||||
template <typename T> class is_tuple_like_ {
|
||||
template <typename U>
|
||||
static auto check(U* p)
|
||||
-> decltype(std::tuple_size<U>::value,
|
||||
(void)std::declval<typename std::tuple_element<0, U>::type>(),
|
||||
int());
|
||||
template <typename> static void check(...);
|
||||
template<typename T>
|
||||
class is_tuple_like_
|
||||
{
|
||||
template<typename U>
|
||||
static auto check(U *p) -> decltype(std::tuple_size<U>::value, (void)std::declval<typename std::tuple_element<0, U>::type>(), int());
|
||||
template<typename>
|
||||
static void check(...);
|
||||
|
||||
public:
|
||||
static FMT_CONSTEXPR_DECL const bool value =
|
||||
!std::is_void<decltype(check<T>(nullptr))>::value;
|
||||
public:
|
||||
static FMT_CONSTEXPR_DECL const bool value = !std::is_void<decltype(check<T>(nullptr))>::value;
|
||||
};
|
||||
|
||||
// Check for integer_sequence
|
||||
#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1900
|
||||
template <typename T, T... N>
|
||||
template<typename T, T... N>
|
||||
using integer_sequence = std::integer_sequence<T, N...>;
|
||||
template <std::size_t... N> using index_sequence = std::index_sequence<N...>;
|
||||
template <std::size_t N>
|
||||
template<std::size_t... N>
|
||||
using index_sequence = std::index_sequence<N...>;
|
||||
template<std::size_t N>
|
||||
using make_index_sequence = std::make_index_sequence<N>;
|
||||
#else
|
||||
template <typename T, T... N> struct integer_sequence {
|
||||
using value_type = T;
|
||||
template<typename T, T... N>
|
||||
struct integer_sequence
|
||||
{
|
||||
using value_type = T;
|
||||
|
||||
static FMT_CONSTEXPR std::size_t size() { return sizeof...(N); }
|
||||
static FMT_CONSTEXPR std::size_t size()
|
||||
{
|
||||
return sizeof...(N);
|
||||
}
|
||||
};
|
||||
|
||||
template <std::size_t... N>
|
||||
template<std::size_t... N>
|
||||
using index_sequence = integer_sequence<std::size_t, N...>;
|
||||
|
||||
template <typename T, std::size_t N, T... Ns>
|
||||
struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...> {};
|
||||
template <typename T, T... Ns>
|
||||
struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...> {};
|
||||
template<typename T, std::size_t N, T... Ns>
|
||||
struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...>
|
||||
{};
|
||||
template<typename T, T... Ns>
|
||||
struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...>
|
||||
{};
|
||||
|
||||
template <std::size_t N>
|
||||
template<std::size_t N>
|
||||
using make_index_sequence = make_integer_sequence<std::size_t, N>;
|
||||
#endif
|
||||
|
||||
template <class Tuple, class F, size_t... Is>
|
||||
void for_each(index_sequence<Is...>, Tuple&& tup, F&& f) FMT_NOEXCEPT {
|
||||
using std::get;
|
||||
// using free function get<I>(T) now.
|
||||
const int _[] = {0, ((void)f(get<Is>(tup)), 0)...};
|
||||
(void)_; // blocks warnings
|
||||
template<class Tuple, class F, size_t... Is>
|
||||
void for_each(index_sequence<Is...>, Tuple &&tup, F &&f) FMT_NOEXCEPT
|
||||
{
|
||||
using std::get;
|
||||
// using free function get<I>(T) now.
|
||||
const int _[] = {0, ((void)f(get<Is>(tup)), 0)...};
|
||||
(void)_; // blocks warnings
|
||||
}
|
||||
|
||||
template <class T>
|
||||
FMT_CONSTEXPR make_index_sequence<std::tuple_size<T>::value> get_indexes(
|
||||
T const&) {
|
||||
return {};
|
||||
template<class T>
|
||||
FMT_CONSTEXPR make_index_sequence<std::tuple_size<T>::value> get_indexes(T const &)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
template <class Tuple, class F> void for_each(Tuple&& tup, F&& f) {
|
||||
const auto indexes = get_indexes(tup);
|
||||
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f));
|
||||
template<class Tuple, class F>
|
||||
void for_each(Tuple &&tup, F &&f)
|
||||
{
|
||||
const auto indexes = get_indexes(tup);
|
||||
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f));
|
||||
}
|
||||
|
||||
template <typename Arg, FMT_ENABLE_IF(!is_like_std_string<
|
||||
typename std::decay<Arg>::type>::value)>
|
||||
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) {
|
||||
return add_space ? " {}" : "{}";
|
||||
template<typename Arg, FMT_ENABLE_IF(!is_like_std_string<typename std::decay<Arg>::type>::value)>
|
||||
FMT_CONSTEXPR const char *format_str_quoted(bool add_space, const Arg &)
|
||||
{
|
||||
return add_space ? " {}" : "{}";
|
||||
}
|
||||
|
||||
template <typename Arg, FMT_ENABLE_IF(is_like_std_string<
|
||||
typename std::decay<Arg>::type>::value)>
|
||||
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) {
|
||||
return add_space ? " \"{}\"" : "\"{}\"";
|
||||
template<typename Arg, FMT_ENABLE_IF(is_like_std_string<typename std::decay<Arg>::type>::value)>
|
||||
FMT_CONSTEXPR const char *format_str_quoted(bool add_space, const Arg &)
|
||||
{
|
||||
return add_space ? " \"{}\"" : "\"{}\"";
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char*) {
|
||||
return add_space ? " \"{}\"" : "\"{}\"";
|
||||
FMT_CONSTEXPR const char *format_str_quoted(bool add_space, const char *)
|
||||
{
|
||||
return add_space ? " \"{}\"" : "\"{}\"";
|
||||
}
|
||||
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t*) {
|
||||
return add_space ? L" \"{}\"" : L"\"{}\"";
|
||||
FMT_CONSTEXPR const wchar_t *format_str_quoted(bool add_space, const wchar_t *)
|
||||
{
|
||||
return add_space ? L" \"{}\"" : L"\"{}\"";
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char) {
|
||||
return add_space ? " '{}'" : "'{}'";
|
||||
FMT_CONSTEXPR const char *format_str_quoted(bool add_space, const char)
|
||||
{
|
||||
return add_space ? " '{}'" : "'{}'";
|
||||
}
|
||||
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t) {
|
||||
return add_space ? L" '{}'" : L"'{}'";
|
||||
FMT_CONSTEXPR const wchar_t *format_str_quoted(bool add_space, const wchar_t)
|
||||
{
|
||||
return add_space ? L" '{}'" : L"'{}'";
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace internal
|
||||
|
||||
template <typename T> struct is_tuple_like {
|
||||
static FMT_CONSTEXPR_DECL const bool value =
|
||||
internal::is_tuple_like_<T>::value && !internal::is_range_<T>::value;
|
||||
template<typename T>
|
||||
struct is_tuple_like
|
||||
{
|
||||
static FMT_CONSTEXPR_DECL const bool value = internal::is_tuple_like_<T>::value && !internal::is_range_<T>::value;
|
||||
};
|
||||
|
||||
template <typename TupleT, typename Char>
|
||||
struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
|
||||
private:
|
||||
// C++11 generic lambda for format()
|
||||
template <typename FormatContext> struct format_each {
|
||||
template <typename T> void operator()(const T& v) {
|
||||
if (i > 0) {
|
||||
if (formatting.add_prepostfix_space) {
|
||||
*out++ = ' ';
|
||||
template<typename TupleT, typename Char>
|
||||
struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>>
|
||||
{
|
||||
private:
|
||||
// C++11 generic lambda for format()
|
||||
template<typename FormatContext>
|
||||
struct format_each
|
||||
{
|
||||
template<typename T>
|
||||
void operator()(const T &v)
|
||||
{
|
||||
if (i > 0)
|
||||
{
|
||||
if (formatting.add_prepostfix_space)
|
||||
{
|
||||
*out++ = ' ';
|
||||
}
|
||||
out = internal::copy(formatting.delimiter, out);
|
||||
}
|
||||
out = format_to(out, internal::format_str_quoted((formatting.add_delimiter_spaces && i > 0), v), v);
|
||||
++i;
|
||||
}
|
||||
out = internal::copy(formatting.delimiter, out);
|
||||
}
|
||||
out = format_to(out,
|
||||
internal::format_str_quoted(
|
||||
(formatting.add_delimiter_spaces && i > 0), v),
|
||||
v);
|
||||
++i;
|
||||
|
||||
formatting_tuple<Char> &formatting;
|
||||
std::size_t &i;
|
||||
typename std::add_lvalue_reference<decltype(std::declval<FormatContext>().out())>::type out;
|
||||
};
|
||||
|
||||
public:
|
||||
formatting_tuple<Char> formatting;
|
||||
|
||||
template<typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin())
|
||||
{
|
||||
return formatting.parse(ctx);
|
||||
}
|
||||
|
||||
formatting_tuple<Char>& formatting;
|
||||
std::size_t& i;
|
||||
typename std::add_lvalue_reference<decltype(
|
||||
std::declval<FormatContext>().out())>::type out;
|
||||
};
|
||||
template<typename FormatContext = format_context>
|
||||
auto format(const TupleT &values, FormatContext &ctx) -> decltype(ctx.out())
|
||||
{
|
||||
auto out = ctx.out();
|
||||
std::size_t i = 0;
|
||||
internal::copy(formatting.prefix, out);
|
||||
|
||||
public:
|
||||
formatting_tuple<Char> formatting;
|
||||
internal::for_each(values, format_each<FormatContext>{formatting, i, out});
|
||||
if (formatting.add_prepostfix_space)
|
||||
{
|
||||
*out++ = ' ';
|
||||
}
|
||||
internal::copy(formatting.postfix, out);
|
||||
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
return formatting.parse(ctx);
|
||||
}
|
||||
|
||||
template <typename FormatContext = format_context>
|
||||
auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) {
|
||||
auto out = ctx.out();
|
||||
std::size_t i = 0;
|
||||
internal::copy(formatting.prefix, out);
|
||||
|
||||
internal::for_each(values, format_each<FormatContext>{formatting, i, out});
|
||||
if (formatting.add_prepostfix_space) {
|
||||
*out++ = ' ';
|
||||
return ctx.out();
|
||||
}
|
||||
internal::copy(formatting.postfix, out);
|
||||
|
||||
return ctx.out();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename Char> struct is_range {
|
||||
static FMT_CONSTEXPR_DECL const bool value =
|
||||
internal::is_range_<T>::value &&
|
||||
!internal::is_like_std_string<T>::value &&
|
||||
!std::is_convertible<T, std::basic_string<Char>>::value &&
|
||||
!std::is_constructible<internal::std_string_view<Char>, T>::value;
|
||||
template<typename T, typename Char>
|
||||
struct is_range
|
||||
{
|
||||
static FMT_CONSTEXPR_DECL const bool value = internal::is_range_<T>::value && !internal::is_like_std_string<T>::value &&
|
||||
!std::is_convertible<T, std::basic_string<Char>>::value &&
|
||||
!std::is_constructible<internal::std_string_view<Char>, T>::value;
|
||||
};
|
||||
|
||||
template <typename RangeT, typename Char>
|
||||
struct formatter<RangeT, Char,
|
||||
enable_if_t<fmt::is_range<RangeT, Char>::value>> {
|
||||
formatting_range<Char> formatting;
|
||||
template<typename RangeT, typename Char>
|
||||
struct formatter<RangeT, Char, enable_if_t<fmt::is_range<RangeT, Char>::value>>
|
||||
{
|
||||
formatting_range<Char> formatting;
|
||||
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
return formatting.parse(ctx);
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
typename FormatContext::iterator format(const RangeT& values,
|
||||
FormatContext& ctx) {
|
||||
auto out = internal::copy(formatting.prefix, ctx.out());
|
||||
std::size_t i = 0;
|
||||
for (auto it = values.begin(), end = values.end(); it != end; ++it) {
|
||||
if (i > 0) {
|
||||
if (formatting.add_prepostfix_space) *out++ = ' ';
|
||||
out = internal::copy(formatting.delimiter, out);
|
||||
}
|
||||
out = format_to(out,
|
||||
internal::format_str_quoted(
|
||||
(formatting.add_delimiter_spaces && i > 0), *it),
|
||||
*it);
|
||||
if (++i > formatting.range_length_limit) {
|
||||
out = format_to(out, " ... <other elements>");
|
||||
break;
|
||||
}
|
||||
template<typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin())
|
||||
{
|
||||
return formatting.parse(ctx);
|
||||
}
|
||||
if (formatting.add_prepostfix_space) *out++ = ' ';
|
||||
return internal::copy(formatting.postfix, out);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char, typename... T> struct tuple_arg_join : internal::view {
|
||||
const std::tuple<T...>& tuple;
|
||||
basic_string_view<Char> sep;
|
||||
|
||||
tuple_arg_join(const std::tuple<T...>& t, basic_string_view<Char> s)
|
||||
: tuple{t}, sep{s} {}
|
||||
};
|
||||
|
||||
template <typename Char, typename... T>
|
||||
struct formatter<tuple_arg_join<Char, T...>, Char> {
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
typename FormatContext::iterator format(
|
||||
const tuple_arg_join<Char, T...>& value, FormatContext& ctx) {
|
||||
return format(value, ctx, internal::make_index_sequence<sizeof...(T)>{});
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename FormatContext, size_t... N>
|
||||
typename FormatContext::iterator format(
|
||||
const tuple_arg_join<Char, T...>& value, FormatContext& ctx,
|
||||
internal::index_sequence<N...>) {
|
||||
return format_args(value, ctx, std::get<N>(value.tuple)...);
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
typename FormatContext::iterator format_args(
|
||||
const tuple_arg_join<Char, T...>&, FormatContext& ctx) {
|
||||
// NOTE: for compilers that support C++17, this empty function instantiation
|
||||
// can be replaced with a constexpr branch in the variadic overload.
|
||||
return ctx.out();
|
||||
}
|
||||
|
||||
template <typename FormatContext, typename Arg, typename... Args>
|
||||
typename FormatContext::iterator format_args(
|
||||
const tuple_arg_join<Char, T...>& value, FormatContext& ctx,
|
||||
const Arg& arg, const Args&... args) {
|
||||
using base = formatter<typename std::decay<Arg>::type, Char>;
|
||||
auto out = ctx.out();
|
||||
out = base{}.format(arg, ctx);
|
||||
if (sizeof...(Args) > 0) {
|
||||
out = std::copy(value.sep.begin(), value.sep.end(), out);
|
||||
ctx.advance_to(out);
|
||||
return format_args(value, ctx, args...);
|
||||
template<typename FormatContext>
|
||||
typename FormatContext::iterator format(const RangeT &values, FormatContext &ctx)
|
||||
{
|
||||
auto out = internal::copy(formatting.prefix, ctx.out());
|
||||
std::size_t i = 0;
|
||||
for (auto it = values.begin(), end = values.end(); it != end; ++it)
|
||||
{
|
||||
if (i > 0)
|
||||
{
|
||||
if (formatting.add_prepostfix_space)
|
||||
*out++ = ' ';
|
||||
out = internal::copy(formatting.delimiter, out);
|
||||
}
|
||||
out = format_to(out, internal::format_str_quoted((formatting.add_delimiter_spaces && i > 0), *it), *it);
|
||||
if (++i > formatting.range_length_limit)
|
||||
{
|
||||
out = format_to(out, " ... <other elements>");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (formatting.add_prepostfix_space)
|
||||
*out++ = ' ';
|
||||
return internal::copy(formatting.postfix, out);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Char, typename... T>
|
||||
struct tuple_arg_join : internal::view
|
||||
{
|
||||
const std::tuple<T...> &tuple;
|
||||
basic_string_view<Char> sep;
|
||||
|
||||
tuple_arg_join(const std::tuple<T...> &t, basic_string_view<Char> s)
|
||||
: tuple{t}
|
||||
, sep{s}
|
||||
{}
|
||||
};
|
||||
|
||||
template<typename Char, typename... T>
|
||||
struct formatter<tuple_arg_join<Char, T...>, Char>
|
||||
{
|
||||
template<typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin())
|
||||
{
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
template<typename FormatContext>
|
||||
typename FormatContext::iterator format(const tuple_arg_join<Char, T...> &value, FormatContext &ctx)
|
||||
{
|
||||
return format(value, ctx, internal::make_index_sequence<sizeof...(T)>{});
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename FormatContext, size_t... N>
|
||||
typename FormatContext::iterator format(const tuple_arg_join<Char, T...> &value, FormatContext &ctx, internal::index_sequence<N...>)
|
||||
{
|
||||
return format_args(value, ctx, std::get<N>(value.tuple)...);
|
||||
}
|
||||
|
||||
template<typename FormatContext>
|
||||
typename FormatContext::iterator format_args(const tuple_arg_join<Char, T...> &, FormatContext &ctx)
|
||||
{
|
||||
// NOTE: for compilers that support C++17, this empty function instantiation
|
||||
// can be replaced with a constexpr branch in the variadic overload.
|
||||
return ctx.out();
|
||||
}
|
||||
|
||||
template<typename FormatContext, typename Arg, typename... Args>
|
||||
typename FormatContext::iterator format_args(
|
||||
const tuple_arg_join<Char, T...> &value, FormatContext &ctx, const Arg &arg, const Args &... args)
|
||||
{
|
||||
using base = formatter<typename std::decay<Arg>::type, Char>;
|
||||
auto out = ctx.out();
|
||||
out = base{}.format(arg, ctx);
|
||||
if (sizeof...(Args) > 0)
|
||||
{
|
||||
out = std::copy(value.sep.begin(), value.sep.end(), out);
|
||||
ctx.advance_to(out);
|
||||
return format_args(value, ctx, args...);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -348,18 +398,18 @@ struct formatter<tuple_arg_join<Char, T...>, Char> {
|
||||
// Output: "1, a"
|
||||
\endrst
|
||||
*/
|
||||
template <typename... T>
|
||||
FMT_CONSTEXPR tuple_arg_join<char, T...> join(const std::tuple<T...>& tuple,
|
||||
string_view sep) {
|
||||
return {tuple, sep};
|
||||
template<typename... T>
|
||||
FMT_CONSTEXPR tuple_arg_join<char, T...> join(const std::tuple<T...> &tuple, string_view sep)
|
||||
{
|
||||
return {tuple, sep};
|
||||
}
|
||||
|
||||
template <typename... T>
|
||||
FMT_CONSTEXPR tuple_arg_join<wchar_t, T...> join(const std::tuple<T...>& tuple,
|
||||
wstring_view sep) {
|
||||
return {tuple, sep};
|
||||
template<typename... T>
|
||||
FMT_CONSTEXPR tuple_arg_join<wchar_t, T...> join(const std::tuple<T...> &tuple, wstring_view sep)
|
||||
{
|
||||
return {tuple, sep};
|
||||
}
|
||||
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_RANGES_H_
|
||||
#endif // FMT_RANGES_H_
|
||||
|
||||
Reference in New Issue
Block a user