This commit is contained in:
Ross Jacobs 2020-04-20 22:59:20 -07:00 committed by GitHub
parent 36257cbfe4
commit 5860c5c80b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
50 changed files with 10078 additions and 8095 deletions

View File

@ -5,3 +5,8 @@ repos:
- id: check-yaml - id: check-yaml
- id: end-of-file-fixer - id: end-of-file-fixer
- id: trailing-whitespace - id: trailing-whitespace
- repo: https://github.com/pocc/pre-commit-hooks
rev: master
hooks:
- id: clang-format
args: [-i, -style=file]

View File

@ -5,8 +5,8 @@
*/ */
#include "IXCobraBot.h" #include "IXCobraBot.h"
#include "IXQueueManager.h"
#include "IXQueueManager.h"
#include <algorithm> #include <algorithm>
#include <chrono> #include <chrono>
#include <ixcobra/IXCobraConnection.h> #include <ixcobra/IXCobraConnection.h>
@ -109,7 +109,6 @@ namespace ix
auto sender = auto sender =
[this, &queueManager, verbose, &sentCount, &stop, &throttled, &fatalCobraError] { [this, &queueManager, verbose, &sentCount, &stop, &throttled, &fatalCobraError] {
while (true) while (true)
{ {
auto data = queueManager.pop(); auto data = queueManager.pop();
@ -119,7 +118,8 @@ namespace ix
if (stop) break; if (stop) break;
if (msg.isNull()) continue; 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 // That might be too noisy
if (verbose) if (verbose)
@ -155,8 +155,7 @@ namespace ix
&fatalCobraError, &fatalCobraError,
&useQueue, &useQueue,
&queueManager, &queueManager,
&sentCount](const CobraEventPtr& event) &sentCount](const CobraEventPtr& event) {
{
if (event->type == ix::CobraEventType::Open) if (event->type == ix::CobraEventType::Open)
{ {
spdlog::info("Subscriber connected"); spdlog::info("Subscriber connected");
@ -178,11 +177,21 @@ namespace ix
conn.subscribe(channel, conn.subscribe(channel,
filter, filter,
subscriptionPosition, subscriptionPosition,
[this, &jsonWriter, verbose, &throttled, &receivedCount, &queueManager, &useQueue, &subscriptionPosition, &fatalCobraError, &sentCount]( [this,
const Json::Value& msg, const std::string& position) { &jsonWriter,
verbose,
&throttled,
&receivedCount,
&queueManager,
&useQueue,
&subscriptionPosition,
&fatalCobraError,
&sentCount](const Json::Value& msg, const std::string& position) {
if (verbose) if (verbose)
{ {
spdlog::info("Subscriber received message {} -> {}", position, jsonWriter.write(msg)); spdlog::info("Subscriber received message {} -> {}",
position,
jsonWriter.write(msg));
} }
subscriptionPosition = position; subscriptionPosition = position;
@ -201,7 +210,9 @@ namespace ix
} }
else else
{ {
if (_onBotMessageCallback && _onBotMessageCallback(msg, position, verbose, throttled, fatalCobraError)) if (_onBotMessageCallback &&
_onBotMessageCallback(
msg, position, verbose, throttled, fatalCobraError))
{ {
// That might be too noisy // That might be too noisy
if (verbose) if (verbose)
@ -268,7 +279,7 @@ namespace ix
// Run for a duration, used by unittesting now // Run for a duration, used by unittesting now
else else
{ {
for (int i = 0 ; i < runtime; ++i) for (int i = 0; i < runtime; ++i)
{ {
auto duration = std::chrono::seconds(1); auto duration = std::chrono::seconds(1);
std::this_thread::sleep_for(duration); std::this_thread::sleep_for(duration);
@ -300,4 +311,4 @@ namespace ix
{ {
_onBotMessageCallback = callback; _onBotMessageCallback = callback;
} }
} } // namespace ix

View File

@ -6,11 +6,11 @@
#pragma once #pragma once
#include <ixcobra/IXCobraConfig.h>
#include <stddef.h>
#include <json/json.h>
#include <functional>
#include <atomic> #include <atomic>
#include <functional>
#include <ixcobra/IXCobraConfig.h>
#include <json/json.h>
#include <stddef.h>
namespace ix namespace ix
{ {
@ -40,4 +40,4 @@ namespace ix
private: private:
OnBotMessageCallback _onBotMessageCallback; OnBotMessageCallback _onBotMessageCallback;
}; };
} } // namespace ix

View File

@ -5,9 +5,9 @@
*/ */
#include "IXCobraToSentryBot.h" #include "IXCobraToSentryBot.h"
#include "IXCobraBot.h" #include "IXCobraBot.h"
#include "IXQueueManager.h" #include "IXQueueManager.h"
#include <chrono> #include <chrono>
#include <ixcobra/IXCobraConnection.h> #include <ixcobra/IXCobraConnection.h>
#include <spdlog/spdlog.h> #include <spdlog/spdlog.h>
@ -31,7 +31,8 @@ namespace ix
const std::string& /*position*/, const std::string& /*position*/,
const bool verbose, const bool verbose,
std::atomic<bool>& throttled, std::atomic<bool>& throttled,
std::atomic<bool>& /*fatalCobraError*/) -> bool { std::atomic<bool> &
/*fatalCobraError*/) -> bool {
auto ret = sentryClient.send(msg, verbose); auto ret = sentryClient.send(msg, verbose);
HttpResponsePtr response = ret.first; HttpResponsePtr response = ret.first;

View File

@ -5,10 +5,10 @@
*/ */
#pragma once #pragma once
#include <cstdint>
#include <ixcobra/IXCobraConfig.h> #include <ixcobra/IXCobraConfig.h>
#include <ixsentry/IXSentryClient.h> #include <ixsentry/IXSentryClient.h>
#include <string> #include <string>
#include <cstdint>
namespace ix namespace ix
{ {

View File

@ -5,10 +5,10 @@
*/ */
#include "IXCobraToStatsdBot.h" #include "IXCobraToStatsdBot.h"
#include "IXCobraBot.h"
#include "IXQueueManager.h" #include "IXQueueManager.h"
#include "IXStatsdClient.h" #include "IXStatsdClient.h"
#include "IXCobraBot.h"
#include <chrono> #include <chrono>
#include <ixcobra/IXCobraConnection.h> #include <ixcobra/IXCobraConnection.h>
#include <spdlog/spdlog.h> #include <spdlog/spdlog.h>
@ -74,73 +74,74 @@ namespace ix
auto tokens = parseFields(fields); auto tokens = parseFields(fields);
CobraBot bot; CobraBot bot;
bot.setOnBotMessageCallback([&statsdClient, &tokens, &gauge, &timer](const Json::Value& msg, bot.setOnBotMessageCallback(
const std::string& /*position*/, [&statsdClient, &tokens, &gauge, &timer](const Json::Value& msg,
const bool verbose, const std::string& /*position*/,
std::atomic<bool>& /*throttled*/, const bool verbose,
std::atomic<bool>& fatalCobraError) -> bool { std::atomic<bool>& /*throttled*/,
std::string id; std::atomic<bool>& fatalCobraError) -> bool {
for (auto&& attr : tokens) std::string id;
{ for (auto&& attr : tokens)
id += "."; {
auto val = extractAttr(attr, msg); id += ".";
id += val.asString(); auto val = extractAttr(attr, msg);
} id += val.asString();
}
if (gauge.empty() && timer.empty()) 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())
{ {
x = (size_t) val.asInt(); statsdClient.count(id, 1);
}
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 else
{ {
spdlog::error("Gauge {} is not a numeric type", gauge); std::string attrName = (!gauge.empty()) ? gauge : timer;
fatalCobraError = true; auto val = extractAttr(attrName, msg);
return false; 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) return true;
{ });
spdlog::info("{} - {} -> {}", id, attrName, x);
}
if (!gauge.empty())
{
statsdClient.gauge(id, x);
}
else
{
statsdClient.timing(id, x);
}
}
return true;
});
bool useQueue = true; bool useQueue = true;

View File

@ -5,11 +5,11 @@
*/ */
#pragma once #pragma once
#include <ixcobra/IXCobraConfig.h>
#include <ixbots/IXStatsdClient.h>
#include <string>
#include <stddef.h>
#include <cstdint> #include <cstdint>
#include <ixbots/IXStatsdClient.h>
#include <ixcobra/IXCobraConfig.h>
#include <stddef.h>
#include <string>
namespace ix namespace ix
{ {

View File

@ -5,13 +5,13 @@
*/ */
#include "IXCobraToStdoutBot.h" #include "IXCobraToStdoutBot.h"
#include "IXCobraBot.h" #include "IXCobraBot.h"
#include "IXQueueManager.h" #include "IXQueueManager.h"
#include <chrono> #include <chrono>
#include <iostream>
#include <spdlog/spdlog.h> #include <spdlog/spdlog.h>
#include <sstream> #include <sstream>
#include <iostream>
namespace ix namespace ix
{ {
@ -79,17 +79,19 @@ namespace ix
CobraBot bot; CobraBot bot;
auto jsonWriter = makeStreamWriter(); auto jsonWriter = makeStreamWriter();
bot.setOnBotMessageCallback([&fluentd, &quiet, &jsonWriter](const Json::Value& msg, bot.setOnBotMessageCallback(
const std::string& position, [&fluentd, &quiet, &jsonWriter](const Json::Value& msg,
const bool /*verbose*/, const std::string& position,
std::atomic<bool>& /*throttled*/, const bool /*verbose*/,
std::atomic<bool>& /*fatalCobraError*/) -> bool { std::atomic<bool>& /*throttled*/,
if (!quiet) std::atomic<bool> &
{ /*fatalCobraError*/) -> bool {
writeToStdout(fluentd, jsonWriter, msg, position); if (!quiet)
} {
return true; writeToStdout(fluentd, jsonWriter, msg, position);
}); }
return true;
});
bool useQueue = false; bool useQueue = false;

View File

@ -5,10 +5,10 @@
*/ */
#pragma once #pragma once
#include <ixcobra/IXCobraConfig.h>
#include <string>
#include <stddef.h>
#include <cstdint> #include <cstdint>
#include <ixcobra/IXCobraConfig.h>
#include <stddef.h>
#include <string>
namespace ix namespace ix
{ {

View File

@ -5,8 +5,9 @@
*/ */
#include "IXQueueManager.h" #include "IXQueueManager.h"
#include <vector>
#include <algorithm> #include <algorithm>
#include <vector>
namespace ix namespace ix
{ {
@ -63,4 +64,4 @@ namespace ix
_condition.notify_one(); _condition.notify_one();
} }
} }
} } // namespace ix

View File

@ -6,12 +6,12 @@
#pragma once #pragma once
#include <stddef.h>
#include <json/json.h>
#include <mutex>
#include <condition_variable> #include <condition_variable>
#include <queue> #include <json/json.h>
#include <map> #include <map>
#include <mutex>
#include <queue>
#include <stddef.h>
namespace ix namespace ix
{ {
@ -32,4 +32,4 @@ namespace ix
std::condition_variable _condition; std::condition_variable _condition;
size_t _maxQueueSize; size_t _maxQueueSize;
}; };
} } // namespace ix

View File

@ -39,24 +39,21 @@
#include "IXStatsdClient.h" #include "IXStatsdClient.h"
#include <iostream>
#include <ixwebsocket/IXNetSystem.h> #include <ixwebsocket/IXNetSystem.h>
#include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <stdio.h>
#include <iostream>
namespace ix namespace ix
{ {
StatsdClient::StatsdClient(const std::string& host, StatsdClient::StatsdClient(const std::string& host, int port, const std::string& prefix)
int port, : _host(host)
const std::string& prefix) , _port(port)
: _host(host) , _prefix(prefix)
, _port(port) , _stop(false)
, _prefix(prefix)
, _stop(false)
{ {
_thread = std::thread([this] _thread = std::thread([this] {
{
while (!_stop) while (!_stop)
{ {
flushQueue(); flushQueue();
@ -119,8 +116,8 @@ namespace ix
cleanup(key); cleanup(key);
char buf[256]; char buf[256];
snprintf(buf, sizeof(buf), "%s%s:%zd|%s\n", snprintf(
_prefix.c_str(), key.c_str(), value, type.c_str()); buf, sizeof(buf), "%s%s:%zd|%s\n", _prefix.c_str(), key.c_str(), value, type.c_str());
enqueue(buf); enqueue(buf);
return 0; return 0;
@ -142,9 +139,7 @@ namespace ix
auto ret = _socket.sendto(message); auto ret = _socket.sendto(message);
if (ret != 0) if (ret != 0)
{ {
std::cerr << "error: " std::cerr << "error: " << strerror(UdpSocket::getErrno()) << std::endl;
<< strerror(UdpSocket::getErrno())
<< std::endl;
} }
_queue.pop_front(); _queue.pop_front();
} }

View File

@ -6,21 +6,20 @@
#pragma once #pragma once
#include <atomic>
#include <deque>
#include <ixwebsocket/IXUdpSocket.h> #include <ixwebsocket/IXUdpSocket.h>
#include <mutex>
#include <string> #include <string>
#include <thread> #include <thread>
#include <deque>
#include <mutex>
#include <atomic>
namespace ix namespace ix
{ {
class StatsdClient class StatsdClient
{ {
public: public:
StatsdClient(const std::string& host="127.0.0.1", StatsdClient(const std::string& host = "127.0.0.1",
int port=8125, int port = 8125,
const std::string& prefix = ""); const std::string& prefix = "");
~StatsdClient(); ~StatsdClient();

View File

@ -6,8 +6,8 @@
#pragma once #pragma once
#include <ixwebsocket/IXWebSocketPerMessageDeflateOptions.h>
#include <ixwebsocket/IXSocketTLSOptions.h> #include <ixwebsocket/IXSocketTLSOptions.h>
#include <ixwebsocket/IXWebSocketPerMessageDeflateOptions.h>
namespace ix namespace ix
{ {

View File

@ -5,17 +5,17 @@
*/ */
#include "IXCobraConnection.h" #include "IXCobraConnection.h"
#include <ixcrypto/IXHMac.h>
#include <ixwebsocket/IXWebSocket.h>
#include <ixwebsocket/IXSocketTLSOptions.h>
#include <algorithm> #include <algorithm>
#include <stdexcept>
#include <cmath>
#include <cassert> #include <cassert>
#include <cmath>
#include <cstring> #include <cstring>
#include <iostream> #include <iostream>
#include <ixcrypto/IXHMac.h>
#include <ixwebsocket/IXSocketTLSOptions.h>
#include <ixwebsocket/IXWebSocket.h>
#include <sstream> #include <sstream>
#include <stdexcept>
namespace ix namespace ix
@ -26,12 +26,12 @@ namespace ix
constexpr CobraConnection::MsgId CobraConnection::kInvalidMsgId; constexpr CobraConnection::MsgId CobraConnection::kInvalidMsgId;
constexpr int CobraConnection::kPingIntervalSecs; constexpr int CobraConnection::kPingIntervalSecs;
CobraConnection::CobraConnection() : CobraConnection::CobraConnection()
_webSocket(new WebSocket()), : _webSocket(new WebSocket())
_publishMode(CobraConnection_PublishMode_Immediate), , _publishMode(CobraConnection_PublishMode_Immediate)
_authenticated(false), , _authenticated(false)
_eventCallback(nullptr), , _eventCallback(nullptr)
_id(1) , _id(1)
{ {
_pdu["action"] = "rtm/publish"; _pdu["action"] = "rtm/publish";
@ -97,11 +97,7 @@ namespace ix
if (_eventCallback) if (_eventCallback)
{ {
_eventCallback( _eventCallback(
std::make_unique<CobraEvent>(eventType, std::make_unique<CobraEvent>(eventType, errorMsg, headers, subscriptionId, msgId));
errorMsg,
headers,
subscriptionId,
msgId));
} }
} }
@ -121,126 +117,119 @@ namespace ix
void CobraConnection::initWebSocketOnMessageCallback() void CobraConnection::initWebSocketOnMessageCallback()
{ {
_webSocket->setOnMessageCallback( _webSocket->setOnMessageCallback([this](const ix::WebSocketMessagePtr& msg) {
[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; 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, invokeErrorCallback("Invalid json", msg->str);
std::string(), return;
msg->openInfo.headers);
sendHandshakeMessage();
} }
else if (msg->type == ix::WebSocketMessageType::Close)
{
_authenticated = false;
std::stringstream ss; if (!data.isMember("action"))
ss << "Close code " << msg->closeInfo.code; {
ss << " reason " << msg->closeInfo.reason; invokeErrorCallback("Missing action", msg->str);
invokeEventCallback(ix::CobraEventType::Closed, return;
ss.str());
} }
else if (msg->type == ix::WebSocketMessageType::Message)
auto action = data["action"].asString();
if (action == "auth/handshake/ok")
{ {
Json::Value data; if (!handleHandshakeResponse(data))
Json::Reader reader;
if (!reader.parse(msg->str, data))
{ {
invokeErrorCallback("Invalid json", msg->str); invokeErrorCallback("Error extracting nonce from handshake response",
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,
msg->str); 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; invokeEventCallback(ix::CobraEventType::HandshakeError, msg->str);
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) 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; return _publishMode;
} }
void CobraConnection::configure(const std::string& appkey, void CobraConnection::configure(
const std::string& endpoint, const std::string& appkey,
const std::string& rolename, const std::string& endpoint,
const std::string& rolesecret, const std::string& rolename,
const WebSocketPerMessageDeflateOptions& webSocketPerMessageDeflateOptions, const std::string& rolesecret,
const SocketTLSOptions& socketTLSOptions) const WebSocketPerMessageDeflateOptions& webSocketPerMessageDeflateOptions,
const SocketTLSOptions& socketTLSOptions)
{ {
_roleName = rolename; _roleName = rolename;
_roleSecret = rolesecret; _roleSecret = rolesecret;
@ -402,7 +392,8 @@ namespace ix
if (!subscriptionId.isString()) return false; if (!subscriptionId.isString()) return false;
invokeEventCallback(ix::CobraEventType::Subscribed, invokeEventCallback(ix::CobraEventType::Subscribed,
std::string(), WebSocketHttpHeaders(), std::string(),
WebSocketHttpHeaders(),
subscriptionId.asString()); subscriptionId.asString());
return true; return true;
} }
@ -420,7 +411,8 @@ namespace ix
if (!subscriptionId.isString()) return false; if (!subscriptionId.isString()) return false;
invokeEventCallback(ix::CobraEventType::UnSubscribed, invokeEventCallback(ix::CobraEventType::UnSubscribed,
std::string(), WebSocketHttpHeaders(), std::string(),
WebSocketHttpHeaders(),
subscriptionId.asString()); subscriptionId.asString());
return true; return true;
} }
@ -468,8 +460,10 @@ namespace ix
uint64_t msgId = id.asUInt64(); uint64_t msgId = id.asUInt64();
invokeEventCallback(ix::CobraEventType::Published, invokeEventCallback(ix::CobraEventType::Published,
std::string(), WebSocketHttpHeaders(), std::string(),
std::string(), msgId); WebSocketHttpHeaders(),
std::string(),
msgId);
invokePublishTrackerCallback(false, true); invokePublishTrackerCallback(false, true);
@ -499,9 +493,7 @@ namespace ix
} }
std::pair<CobraConnection::MsgId, std::string> CobraConnection::prePublish( std::pair<CobraConnection::MsgId, std::string> CobraConnection::prePublish(
const Json::Value& channels, const Json::Value& channels, const Json::Value& msg, bool addToQueue)
const Json::Value& msg,
bool addToQueue)
{ {
std::lock_guard<std::mutex> lock(_prePublishMutex); std::lock_guard<std::mutex> lock(_prePublishMutex);
@ -667,8 +659,7 @@ namespace ix
bool CobraConnection::publishMessage(const std::string& serializedJson) bool CobraConnection::publishMessage(const std::string& serializedJson)
{ {
auto webSocketSendInfo = _webSocket->send(serializedJson); auto webSocketSendInfo = _webSocket->send(serializedJson);
CobraConnection::invokeTrafficTrackerCallback(webSocketSendInfo.wireSize, CobraConnection::invokeTrafficTrackerCallback(webSocketSendInfo.wireSize, false);
false);
return webSocketSendInfo.success; return webSocketSendInfo.success;
} }

View File

@ -6,20 +6,19 @@
#pragma once #pragma once
#include "IXCobraConfig.h"
#include "IXCobraEvent.h"
#include "IXCobraEventType.h"
#include <ixwebsocket/IXWebSocketHttpHeaders.h> #include <ixwebsocket/IXWebSocketHttpHeaders.h>
#include <ixwebsocket/IXWebSocketPerMessageDeflateOptions.h> #include <ixwebsocket/IXWebSocketPerMessageDeflateOptions.h>
#include "IXCobraEventType.h"
#include "IXCobraEvent.h"
#include <json/json.h> #include <json/json.h>
#include <limits>
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#include <queue> #include <queue>
#include <string> #include <string>
#include <thread> #include <thread>
#include <unordered_map> #include <unordered_map>
#include <limits>
#include "IXCobraConfig.h"
#ifdef max #ifdef max
#undef max #undef max
@ -121,10 +120,9 @@ namespace ix
/// Prepare a message for transmission /// Prepare a message for transmission
/// (update the pdu, compute a msgId, serialize json to a string) /// (update the pdu, compute a msgId, serialize json to a string)
std::pair<CobraConnection::MsgId, std::string> prePublish( std::pair<CobraConnection::MsgId, std::string> prePublish(const Json::Value& channels,
const Json::Value& channels, const Json::Value& msg,
const Json::Value& msg, bool addToQueue);
bool addToQueue);
/// Attempt to send next message from the internal queue /// Attempt to send next message from the internal queue
bool publishNext(); bool publishNext();

View File

@ -7,10 +7,10 @@
#pragma once #pragma once
#include "IXCobraEventType.h" #include "IXCobraEventType.h"
#include <cstdint>
#include <ixwebsocket/IXWebSocketHttpHeaders.h> #include <ixwebsocket/IXWebSocketHttpHeaders.h>
#include <memory> #include <memory>
#include <string> #include <string>
#include <cstdint>
namespace ix namespace ix
{ {
@ -38,4 +38,4 @@ namespace ix
}; };
using CobraEventPtr = std::unique_ptr<CobraEvent>; using CobraEventPtr = std::unique_ptr<CobraEvent>;
} } // namespace ix

View File

@ -5,9 +5,9 @@
*/ */
#include "IXCobraMetricsPublisher.h" #include "IXCobraMetricsPublisher.h"
#include <ixwebsocket/IXSocketTLSOptions.h>
#include <algorithm> #include <algorithm>
#include <ixwebsocket/IXSocketTLSOptions.h>
#include <stdexcept> #include <stdexcept>
@ -17,8 +17,8 @@ namespace ix
const std::string CobraMetricsPublisher::kSetRateControlId = "sms_set_rate_control_id"; const std::string CobraMetricsPublisher::kSetRateControlId = "sms_set_rate_control_id";
const std::string CobraMetricsPublisher::kSetBlacklistId = "sms_set_blacklist_id"; const std::string CobraMetricsPublisher::kSetBlacklistId = "sms_set_blacklist_id";
CobraMetricsPublisher::CobraMetricsPublisher() : CobraMetricsPublisher::CobraMetricsPublisher()
_enabled(true) : _enabled(true)
{ {
} }
@ -27,8 +27,7 @@ namespace ix
; ;
} }
void CobraMetricsPublisher::configure(const CobraConfig& config, void CobraMetricsPublisher::configure(const CobraConfig& config, const std::string& channel)
const std::string& channel)
{ {
// Configure the satori connection and start its publish background thread // Configure the satori connection and start its publish background thread
_cobra_metrics_theaded_publisher.configure(config, channel); _cobra_metrics_theaded_publisher.configure(config, channel);
@ -42,7 +41,7 @@ namespace ix
} }
void CobraMetricsPublisher::setGenericAttributes(const std::string& attrName, void CobraMetricsPublisher::setGenericAttributes(const std::string& attrName,
const Json::Value& value) const Json::Value& value)
{ {
std::lock_guard<std::mutex> lock(_device_mutex); std::lock_guard<std::mutex> lock(_device_mutex);
_device[attrName] = value; _device[attrName] = value;
@ -107,8 +106,7 @@ namespace ix
auto last_update = _last_update.find(id); auto last_update = _last_update.find(id);
if (last_update == _last_update.end()) return false; if (last_update == _last_update.end()) return false;
auto timeDeltaFromLastSend = auto timeDeltaFromLastSend = std::chrono::steady_clock::now() - last_update->second;
std::chrono::steady_clock::now() - last_update->second;
return timeDeltaFromLastSend < std::chrono::seconds(rate_control_it->second); return timeDeltaFromLastSend < std::chrono::seconds(rate_control_it->second);
} }
@ -123,8 +121,7 @@ namespace ix
{ {
auto now = std::chrono::system_clock::now(); auto now = std::chrono::system_clock::now();
auto ms = auto ms =
std::chrono::duration_cast<std::chrono::milliseconds>( std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count();
now.time_since_epoch()).count();
return ms; return ms;
} }
@ -165,10 +162,9 @@ namespace ix
return true; return true;
} }
CobraConnection::MsgId CobraMetricsPublisher::push( CobraConnection::MsgId CobraMetricsPublisher::push(const std::string& id,
const std::string& id, const Json::Value& data,
const Json::Value& data, bool shouldPushTest)
bool shouldPushTest)
{ {
if (shouldPushTest && !shouldPush(id)) return CobraConnection::kInvalidMsgId; if (shouldPushTest && !shouldPush(id)) return CobraConnection::kInvalidMsgId;

View File

@ -40,8 +40,7 @@ namespace ix
/// Configuration / set keys, etc... /// Configuration / set keys, etc...
/// All input data but the channel name is encrypted with rc4 /// All input data but the channel name is encrypted with rc4
void configure(const CobraConfig& config, void configure(const CobraConfig& config, const std::string& channel);
const std::string& channel);
/// Setter for the list of blacklisted metrics ids. /// Setter for the list of blacklisted metrics ids.
/// That list is sorted internally for fast lookups /// 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 /// 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 /// 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. /// 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. /// 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. /// Tells whether a metric can be pushed.
/// A metric can be pushed if it satisfies those conditions: /// 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); void setGenericAttributes(const std::string& attrName, const Json::Value& value);
/// Set a unique id for the session. A uuid can be used. /// 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 /// 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) /// Return the number of milliseconds since the epoch (~1970)
uint64_t getMillisecondsSinceEpoch() const; uint64_t getMillisecondsSinceEpoch() const;

View File

@ -5,78 +5,77 @@
*/ */
#include "IXCobraMetricsThreadedPublisher.h" #include "IXCobraMetricsThreadedPublisher.h"
#include <ixwebsocket/IXSetThreadName.h>
#include <ixwebsocket/IXSocketTLSOptions.h>
#include <ixcore/utils/IXCoreLogger.h>
#include <algorithm> #include <algorithm>
#include <stdexcept>
#include <cmath>
#include <cassert> #include <cassert>
#include <cmath>
#include <iostream> #include <iostream>
#include <ixcore/utils/IXCoreLogger.h>
#include <ixwebsocket/IXSetThreadName.h>
#include <ixwebsocket/IXSocketTLSOptions.h>
#include <sstream> #include <sstream>
#include <stdexcept>
namespace ix namespace ix
{ {
CobraMetricsThreadedPublisher::CobraMetricsThreadedPublisher() : CobraMetricsThreadedPublisher::CobraMetricsThreadedPublisher()
_stop(false) : _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) ix::IXCoreLogger::Log(ss.str().c_str());
{
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());
}); });
} }
@ -105,7 +104,6 @@ namespace ix
_channel = channel; _channel = channel;
_cobra_connection.configure(config); _cobra_connection.configure(config);
} }
void CobraMetricsThreadedPublisher::pushMessage(MessageKind messageKind) void CobraMetricsThreadedPublisher::pushMessage(MessageKind messageKind)
@ -163,13 +161,15 @@ namespace ix
{ {
_cobra_connection.suspend(); _cobra_connection.suspend();
continue; continue;
}; break; };
break;
case MessageKind::Resume: case MessageKind::Resume:
{ {
_cobra_connection.resume(); _cobra_connection.resume();
continue; continue;
}; break; };
break;
case MessageKind::Message: case MessageKind::Message:
{ {
@ -177,7 +177,8 @@ namespace ix
{ {
_cobra_connection.publishNext(); _cobra_connection.publishNext();
} }
}; break; };
break;
} }
} }
} }

View File

@ -27,8 +27,7 @@ namespace ix
~CobraMetricsThreadedPublisher(); ~CobraMetricsThreadedPublisher();
/// Configuration / set keys, etc... /// Configuration / set keys, etc...
void configure(const CobraConfig& config, void configure(const CobraConfig& config, const std::string& channel);
const std::string& channel);
/// Start the worker thread, used for background publishing /// Start the worker thread, used for background publishing
void start(); void start();

View File

@ -3,12 +3,12 @@
namespace ix namespace ix
{ {
// Default do nothing logger // Default do nothing logger
IXCoreLogger::LogFunc IXCoreLogger::_currentLogger = [](const char* /*msg*/){}; IXCoreLogger::LogFunc IXCoreLogger::_currentLogger = [](const char* /*msg*/) {};
void IXCoreLogger::Log(const char* msg) void IXCoreLogger::Log(const char* msg)
{ {
_currentLogger(msg); _currentLogger(msg);
} }
} // ix } // namespace ix

View File

@ -9,7 +9,10 @@ namespace ix
using LogFunc = std::function<void(const char*)>; using LogFunc = std::function<void(const char*)>;
static void Log(const char* msg); static void Log(const char* msg);
static void setLogFunction(LogFunc& func) { _currentLogger = func; } static void setLogFunction(LogFunc& func)
{
_currentLogger = func;
}
private: private:
static LogFunc _currentLogger; static LogFunc _currentLogger;

View File

@ -29,10 +29,9 @@
namespace ix namespace ix
{ {
static const std::string base64_chars = static const std::string base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz"
"abcdefghijklmnopqrstuvwxyz" "0123456789+/";
"0123456789+/";
std::string base64_encode(const std::string& data, size_t len) 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_3[3];
unsigned char char_array_4[4]; unsigned char char_array_4[4];
while(len--) while (len--)
{ {
char_array_3[i++] = *(bytes_to_encode++); 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[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[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[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f; 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]]; ret += base64_chars[char_array_4[i]];
i = 0; 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_3[j] = '\0';
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; 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[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f; 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]]; ret += base64_chars[char_array_4[j]];
while((i++ < 3)) while ((i++ < 3))
ret += '='; ret += '=';
} }
return ret; return ret;
@ -95,7 +93,7 @@ namespace ix
std::string base64_decode(const std::string& encoded_string) 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 i = 0;
int j = 0; int j = 0;
int in_ = 0; int in_ = 0;
@ -103,40 +101,42 @@ namespace ix
std::string ret; std::string ret;
ret.reserve(((in_len + 3) / 4) * 3); 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_++; char_array_4[i++] = encoded_string[in_];
if(i ==4) 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_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[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[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]; 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]; ret += char_array_3[i];
i = 0; i = 0;
} }
} }
if(i) if (i)
{ {
for(j = i; j <4; j++) for (j = i; j < 4; j++)
char_array_4[j] = 0; 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_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[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[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]; 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; return ret;
} }
} } // namespace ix

View File

@ -5,16 +5,17 @@
*/ */
#include "IXHMac.h" #include "IXHMac.h"
#include "IXBase64.h" #include "IXBase64.h"
#if defined(IXCRYPTO_USE_MBED_TLS) #if defined(IXCRYPTO_USE_MBED_TLS)
# include <mbedtls/md.h> #include <mbedtls/md.h>
#elif defined(__APPLE__) #elif defined(__APPLE__)
# include <CommonCrypto/CommonHMAC.h> #include <CommonCrypto/CommonHMAC.h>
#elif defined(IXCRYPTO_USE_OPEN_SSL) #elif defined(IXCRYPTO_USE_OPEN_SSL)
# include <openssl/hmac.h> #include <openssl/hmac.h>
#else #else
# include <assert.h> #include <assert.h>
#endif #endif
namespace ix namespace ix
@ -26,19 +27,21 @@ namespace ix
#if defined(IXCRYPTO_USE_MBED_TLS) #if defined(IXCRYPTO_USE_MBED_TLS)
mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_MD5), mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_MD5),
(unsigned char *) key.c_str(), key.size(), (unsigned char*) key.c_str(),
(unsigned char *) data.c_str(), data.size(), key.size(),
(unsigned char *) &hash); (unsigned char*) data.c_str(),
data.size(),
(unsigned char*) &hash);
#elif defined(__APPLE__) #elif defined(__APPLE__)
CCHmac(kCCHmacAlgMD5, CCHmac(kCCHmacAlgMD5, key.c_str(), key.size(), data.c_str(), data.size(), &hash);
key.c_str(), key.size(),
data.c_str(), data.size(),
&hash);
#elif defined(IXCRYPTO_USE_OPEN_SSL) #elif defined(IXCRYPTO_USE_OPEN_SSL)
HMAC(EVP_md5(), HMAC(EVP_md5(),
key.c_str(), (int) key.size(), key.c_str(),
(unsigned char *) data.c_str(), (int) data.size(), (int) key.size(),
(unsigned char *) hash, nullptr); (unsigned char*) data.c_str(),
(int) data.size(),
(unsigned char*) hash,
nullptr);
#else #else
assert(false && "hmac not implemented on this platform"); assert(false && "hmac not implemented on this platform");
#endif #endif
@ -47,4 +50,4 @@ namespace ix
return base64_encode(hashString, (uint32_t) hashString.size()); return base64_encode(hashString, (uint32_t) hashString.size());
} }
} } // namespace ix

View File

@ -19,4 +19,4 @@ namespace ix
return hashAddress; return hashAddress;
} }
} } // namespace ix

View File

@ -16,23 +16,23 @@
#include "IXUuid.h" #include "IXUuid.h"
#include <sstream>
#include <string>
#include <iomanip> #include <iomanip>
#include <random> #include <random>
#include <sstream>
#include <string>
namespace ix namespace ix
{ {
class Uuid class Uuid
{ {
public: public:
Uuid(); Uuid();
std::string toString() const; std::string toString() const;
private: private:
uint64_t _ab; uint64_t _ab;
uint64_t _cd; uint64_t _cd;
}; };
Uuid::Uuid() Uuid::Uuid()
@ -60,7 +60,7 @@ namespace ix
ss << std::setw(8) << (a); ss << std::setw(8) << (a);
ss << std::setw(4) << (b >> 16); ss << std::setw(4) << (b >> 16);
ss << std::setw(4) << (b & 0xFFFF); 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(4) << (c & 0xFFFF);
ss << std::setw(8) << d; ss << std::setw(8) << d;
@ -72,4 +72,4 @@ namespace ix
Uuid id; Uuid id;
return id.toString(); return id.toString();
} }
} } // namespace ix

View File

@ -7,12 +7,12 @@
#include "IXSentryClient.h" #include "IXSentryClient.h"
#include <chrono> #include <chrono>
#include <iostream>
#include <fstream> #include <fstream>
#include <sstream> #include <iostream>
#include <ixcore/utils/IXCoreLogger.h>
#include <ixwebsocket/IXWebSocketHttpHeaders.h> #include <ixwebsocket/IXWebSocketHttpHeaders.h>
#include <ixwebsocket/IXWebSocketVersion.h> #include <ixwebsocket/IXWebSocketVersion.h>
#include <ixcore/utils/IXCoreLogger.h> #include <sstream>
namespace ix namespace ix
@ -246,24 +246,21 @@ namespace ix
std::string SentryClient::computeUrl(const std::string& project, const std::string& key) std::string SentryClient::computeUrl(const std::string& project, const std::string& key)
{ {
std::stringstream ss; std::stringstream ss;
ss << "https://sentry.io/api/" ss << "https://sentry.io/api/" << project << "/minidump?sentry_key=" << key;
<< project
<< "/minidump?sentry_key="
<< key;
return ss.str(); 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( void SentryClient::uploadMinidump(const std::string& sentryMetadata,
const std::string& sentryMetadata, const std::string& minidumpBytes,
const std::string& minidumpBytes, const std::string& project,
const std::string& project, const std::string& key,
const std::string& key, bool verbose,
bool verbose, const OnResponseCallback& onResponseCallback)
const OnResponseCallback& onResponseCallback)
{ {
std::string multipartBoundary = _httpClient->generateMultipartBoundary(); std::string multipartBoundary = _httpClient->generateMultipartBoundary();
@ -283,15 +280,15 @@ namespace ix
httpParameters["sentry"] = sentryMetadata; httpParameters["sentry"] = sentryMetadata;
args->url = computeUrl(project, key); args->url = computeUrl(project, key);
args->body = _httpClient->serializeHttpFormDataParameters(multipartBoundary, httpFormDataParameters, httpParameters); args->body = _httpClient->serializeHttpFormDataParameters(
multipartBoundary, httpFormDataParameters, httpParameters);
_httpClient->performRequest(args, onResponseCallback); _httpClient->performRequest(args, onResponseCallback);
} }
void SentryClient::uploadPayload( void SentryClient::uploadPayload(const Json::Value& payload,
const Json::Value& payload, bool verbose,
bool verbose, const OnResponseCallback& onResponseCallback)
const OnResponseCallback& onResponseCallback)
{ {
auto args = _httpClient->createRequest(); auto args = _httpClient->createRequest();
args->extraHeaders["X-Sentry-Auth"] = SentryClient::computeAuthHeader(); args->extraHeaders["X-Sentry-Auth"] = SentryClient::computeAuthHeader();

View File

@ -10,8 +10,8 @@
#include <ixwebsocket/IXHttpClient.h> #include <ixwebsocket/IXHttpClient.h>
#include <ixwebsocket/IXSocketTLSOptions.h> #include <ixwebsocket/IXSocketTLSOptions.h>
#include <json/json.h> #include <json/json.h>
#include <regex>
#include <memory> #include <memory>
#include <regex>
namespace ix namespace ix
{ {
@ -28,18 +28,16 @@ namespace ix
// Mostly for testing // Mostly for testing
void setTLSOptions(const SocketTLSOptions& tlsOptions); void setTLSOptions(const SocketTLSOptions& tlsOptions);
void uploadMinidump( void uploadMinidump(const std::string& sentryMetadata,
const std::string& sentryMetadata, const std::string& minidumpBytes,
const std::string& minidumpBytes, const std::string& project,
const std::string& project, const std::string& key,
const std::string& key, bool verbose,
bool verbose, const OnResponseCallback& onResponseCallback);
const OnResponseCallback& onResponseCallback);
void uploadPayload( void uploadPayload(const Json::Value& payload,
const Json::Value& payload, bool verbose,
bool verbose, const OnResponseCallback& onResponseCallback);
const OnResponseCallback& onResponseCallback);
private: private:
int64_t getTimestamp(); int64_t getTimestamp();

View File

@ -6,10 +6,10 @@
#pragma once #pragma once
#include <ixwebsocket/IXSocketTLSOptions.h>
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
#include <string> #include <string>
#include <vector> #include <vector>
#include <ixwebsocket/IXSocketTLSOptions.h>
namespace snake namespace snake
{ {

View File

@ -28,10 +28,7 @@ namespace ix
return false; return false;
} }
CancellationRequest cancellationRequest = []() -> bool CancellationRequest cancellationRequest = []() -> bool { return false; };
{
return false;
};
std::string errMsg; std::string errMsg;
return _socket->connect(hostname, port, errMsg, cancellationRequest); return _socket->connect(hostname, port, errMsg, cancellationRequest);
@ -252,9 +249,8 @@ namespace ix
return true; return true;
} }
std::string RedisClient::prepareXaddCommand( std::string RedisClient::prepareXaddCommand(const std::string& stream,
const std::string& stream, const std::string& message)
const std::string& message)
{ {
std::stringstream ss; std::stringstream ss;
ss << "*5\r\n"; ss << "*5\r\n";
@ -328,7 +324,9 @@ namespace ix
return streamId; 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); bool sent = _socket->writeBytes(commands, nullptr);
if (!sent) if (!sent)

View File

@ -8,11 +8,10 @@
#include <atomic> #include <atomic>
#include <functional> #include <functional>
#include <ixwebsocket/IXSocket.h>
#include <memory> #include <memory>
#include <string> #include <string>
#include <ixwebsocket/IXSocket.h>
namespace ix namespace ix
{ {
class RedisClient class RedisClient
@ -39,14 +38,11 @@ namespace ix
const OnRedisSubscribeCallback& callback); const OnRedisSubscribeCallback& callback);
// XADD // XADD
std::string xadd( std::string xadd(const std::string& channel,
const std::string& channel, const std::string& message,
const std::string& message, std::string& errMsg);
std::string& errMsg);
std::string prepareXaddCommand( std::string prepareXaddCommand(const std::string& stream, const std::string& message);
const std::string& stream,
const std::string& message);
std::string readXaddReply(std::string& errMsg); std::string readXaddReply(std::string& errMsg);

View File

@ -6,17 +6,18 @@
#include "IXRedisServer.h" #include "IXRedisServer.h"
#include <ixwebsocket/IXNetSystem.h>
#include <ixwebsocket/IXSocketConnect.h>
#include <ixwebsocket/IXSocket.h>
#include <ixwebsocket/IXCancellationRequest.h>
#include <fstream> #include <fstream>
#include <ixwebsocket/IXCancellationRequest.h>
#include <ixwebsocket/IXNetSystem.h>
#include <ixwebsocket/IXSocket.h>
#include <ixwebsocket/IXSocketConnect.h>
#include <sstream> #include <sstream>
#include <vector> #include <vector>
namespace ix 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) : SocketServer(port, host, backlog, maxConnections, addressFamily)
, _connectedClientsCount(0) , _connectedClientsCount(0)
, _stopHandlingConnections(false) , _stopHandlingConnections(false)
@ -114,8 +115,7 @@ namespace ix
for (auto it : _subscribers) for (auto it : _subscribers)
{ {
std::stringstream ss; std::stringstream ss;
ss << "Subscription id: " << it.first ss << "Subscription id: " << it.first << " #subscribers: " << it.second.size();
<< " #subscribers: " << it.second.size();
logInfo(ss.str()); logInfo(ss.str());
} }
@ -126,8 +126,7 @@ namespace ix
return _connectedClientsCount; return _connectedClientsCount;
} }
bool RedisServer::startsWith(const std::string& str, bool RedisServer::startsWith(const std::string& str, const std::string& start)
const std::string& start)
{ {
return str.compare(0, start.length(), start) == 0; return str.compare(0, start.length(), start) == 0;
} }
@ -144,9 +143,8 @@ namespace ix
return ss.str(); return ss.str();
} }
bool RedisServer::parseRequest( bool RedisServer::parseRequest(std::unique_ptr<Socket>& socket,
std::unique_ptr<Socket>& socket, std::vector<std::string>& tokens)
std::vector<std::string>& tokens)
{ {
// Parse first line // Parse first line
auto cb = makeCancellationRequestWithTimeout(30, _stopHandlingConnections); auto cb = makeCancellationRequestWithTimeout(30, _stopHandlingConnections);
@ -190,9 +188,8 @@ namespace ix
return true; return true;
} }
bool RedisServer::handleCommand( bool RedisServer::handleCommand(std::unique_ptr<Socket>& socket,
std::unique_ptr<Socket>& socket, const std::vector<std::string>& tokens)
const std::vector<std::string>& tokens)
{ {
if (tokens.size() != 1) return false; if (tokens.size() != 1) return false;
@ -207,31 +204,30 @@ namespace ix
// //
ss << "*6\r\n"; ss << "*6\r\n";
ss << writeString("publish"); // 1 ss << writeString("publish"); // 1
ss << ":3\r\n"; // 2 ss << ":3\r\n"; // 2
ss << "*0\r\n"; // 3 ss << "*0\r\n"; // 3
ss << ":1\r\n"; // 4 ss << ":1\r\n"; // 4
ss << ":2\r\n"; // 5 ss << ":2\r\n"; // 5
ss << ":1\r\n"; // 6 ss << ":1\r\n"; // 6
// //
// subscribe // subscribe
// //
ss << "*6\r\n"; ss << "*6\r\n";
ss << writeString("subscribe"); // 1 ss << writeString("subscribe"); // 1
ss << ":2\r\n"; // 2 ss << ":2\r\n"; // 2
ss << "*0\r\n"; // 3 ss << "*0\r\n"; // 3
ss << ":1\r\n"; // 4 ss << ":1\r\n"; // 4
ss << ":1\r\n"; // 5 ss << ":1\r\n"; // 5
ss << ":1\r\n"; // 6 ss << ":1\r\n"; // 6
socket->writeBytes(ss.str(), cb); socket->writeBytes(ss.str(), cb);
return true; return true;
} }
bool RedisServer::handleSubscribe( bool RedisServer::handleSubscribe(std::unique_ptr<Socket>& socket,
std::unique_ptr<Socket>& socket, const std::vector<std::string>& tokens)
const std::vector<std::string>& tokens)
{ {
if (tokens.size() != 2) return false; if (tokens.size() != 2) return false;
@ -250,9 +246,8 @@ namespace ix
return true; return true;
} }
bool RedisServer::handlePublish( bool RedisServer::handlePublish(std::unique_ptr<Socket>& socket,
std::unique_ptr<Socket>& socket, const std::vector<std::string>& tokens)
const std::vector<std::string>& tokens)
{ {
if (tokens.size() != 3) return false; if (tokens.size() != 3) return false;
@ -281,9 +276,7 @@ namespace ix
// return the number of clients that received the message. // return the number of clients that received the message.
std::stringstream ss; std::stringstream ss;
ss << ":" ss << ":" << std::to_string(subscribers.size()) << "\r\n";
<< std::to_string(subscribers.size())
<< "\r\n";
socket->writeBytes(ss.str(), cb); socket->writeBytes(ss.str(), cb);
return true; return true;

View File

@ -6,13 +6,13 @@
#pragma once #pragma once
#include "IXSocketServer.h"
#include "IXSocket.h" #include "IXSocket.h"
#include "IXSocketServer.h"
#include <functional> #include <functional>
#include <map>
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#include <set> #include <set>
#include <map>
#include <string> #include <string>
#include <thread> #include <thread>
#include <utility> // pair #include <utility> // pair
@ -50,18 +50,14 @@ namespace ix
bool startsWith(const std::string& str, const std::string& start); bool startsWith(const std::string& str, const std::string& start);
std::string writeString(const std::string& str); std::string writeString(const std::string& str);
bool parseRequest( bool parseRequest(std::unique_ptr<Socket>& socket, std::vector<std::string>& tokens);
std::unique_ptr<Socket>& socket,
std::vector<std::string>& tokens);
bool handlePublish(std::unique_ptr<Socket>& socket, bool handlePublish(std::unique_ptr<Socket>& socket, const std::vector<std::string>& tokens);
const std::vector<std::string>& tokens);
bool handleSubscribe(std::unique_ptr<Socket>& socket, bool handleSubscribe(std::unique_ptr<Socket>& socket,
const std::vector<std::string>& tokens); const std::vector<std::string>& tokens);
bool handleCommand(std::unique_ptr<Socket>& socket, bool handleCommand(std::unique_ptr<Socket>& socket, const std::vector<std::string>& tokens);
const std::vector<std::string>& tokens);
void cleanupSubscribers(std::unique_ptr<Socket>& socket); void cleanupSubscribers(std::unique_ptr<Socket>& socket);
}; };

View File

@ -10,9 +10,9 @@
#include "IXSnakeConnectionState.h" #include "IXSnakeConnectionState.h"
#include "nlohmann/json.hpp" #include "nlohmann/json.hpp"
#include <iostream> #include <iostream>
#include <ixcore/utils/IXCoreLogger.h>
#include <ixcrypto/IXHMac.h> #include <ixcrypto/IXHMac.h>
#include <ixwebsocket/IXWebSocket.h> #include <ixwebsocket/IXWebSocket.h>
#include <ixcore/utils/IXCoreLogger.h>
#include <sstream> #include <sstream>
namespace snake namespace snake
@ -189,7 +189,8 @@ namespace snake
nlohmann::json response = { nlohmann::json response = {
{"action", "rtm/subscription/data"}, {"action", "rtm/subscription/data"},
{"id", id++}, {"id", id++},
{"body", {{"subscription_id", subscriptionId}, {"position", "0-0"}, {"messages", {msg}}}}}; {"body",
{{"subscription_id", subscriptionId}, {"position", "0-0"}, {"messages", {msg}}}}};
ws->sendText(response.dump()); ws->sendText(response.dump());
}; };
@ -261,8 +262,7 @@ namespace snake
std::stringstream ss; std::stringstream ss;
ss << "malformed json pdu: " << e.what() << " -> " << str << ""; ss << "malformed json pdu: " << e.what() << " -> " << str << "";
nlohmann::json response = {{"body", {{"error", "invalid_json"}, nlohmann::json response = {{"body", {{"error", "invalid_json"}, {"reason", ss.str()}}}};
{"reason", ss.str()}}}};
ws->sendText(response.dump()); ws->sendText(response.dump());
return; return;
} }

View File

@ -10,8 +10,8 @@
#include "IXSnakeConnectionState.h" #include "IXSnakeConnectionState.h"
#include "IXSnakeProtocol.h" #include "IXSnakeProtocol.h"
#include <iostream> #include <iostream>
#include <sstream>
#include <ixcore/utils/IXCoreLogger.h> #include <ixcore/utils/IXCoreLogger.h>
#include <sstream>
namespace snake namespace snake

View File

@ -31,11 +31,7 @@ TEST_CASE("dns", "[net]")
auto dnsLookup = std::make_shared<DNSLookup>("wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww", 80); auto dnsLookup = std::make_shared<DNSLookup>("wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww", 80);
std::string errMsg; std::string errMsg;
struct addrinfo* res = dnsLookup->resolve(errMsg, struct addrinfo* res = dnsLookup->resolve(errMsg, [] { return false; });
[]
{
return false;
});
std::cerr << "Error message: " << errMsg << std::endl; std::cerr << "Error message: " << errMsg << std::endl;
REQUIRE(res == nullptr); REQUIRE(res == nullptr);
} }
@ -46,11 +42,7 @@ TEST_CASE("dns", "[net]")
std::string errMsg; std::string errMsg;
// The callback returning true means we are requesting cancellation // The callback returning true means we are requesting cancellation
struct addrinfo* res = dnsLookup->resolve(errMsg, struct addrinfo* res = dnsLookup->resolve(errMsg, [] { return true; });
[]
{
return true;
});
std::cerr << "Error message: " << errMsg << std::endl; std::cerr << "Error message: " << errMsg << std::endl;
REQUIRE(res == nullptr); REQUIRE(res == nullptr);
} }

View File

@ -1,4 +1,2 @@
{ DisableFormat: true
"DisableFormat": true, SortIncludes: false
"SortIncludes": false
}

View File

@ -74,7 +74,7 @@ template<typename T>
inline void pad3(T n, memory_buf_t &dest) inline void pad3(T n, memory_buf_t &dest)
{ {
static_assert(std::is_unsigned<T>::value, "pad3 must get unsigned T"); 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')); dest.push_back(static_cast<char>(n / 100 + '0'));
n = n % 100; n = n % 100;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -14,64 +14,53 @@
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace internal { namespace internal {
template <typename Char> template<typename Char>
typename buffer_context<Char>::iterator vformat_to( typename buffer_context<Char>::iterator vformat_to(
const std::locale& loc, buffer<Char>& buf, const std::locale &loc, buffer<Char> &buf, basic_string_view<Char> format_str, basic_format_args<buffer_context<Char>> args)
basic_string_view<Char> format_str, {
basic_format_args<buffer_context<Char>> args) { using range = buffer_range<Char>;
using range = buffer_range<Char>; return vformat_to<arg_formatter<range>>(buf, to_string_view(format_str), args, internal::locale_ref(loc));
return vformat_to<arg_formatter<range>>(buf, to_string_view(format_str), args,
internal::locale_ref(loc));
} }
template <typename Char> template<typename Char>
std::basic_string<Char> vformat(const std::locale& loc, std::basic_string<Char> vformat(const std::locale &loc, basic_string_view<Char> format_str, basic_format_args<buffer_context<Char>> args)
basic_string_view<Char> format_str, {
basic_format_args<buffer_context<Char>> args) { basic_memory_buffer<Char> buffer;
basic_memory_buffer<Char> buffer; internal::vformat_to(loc, buffer, format_str, args);
internal::vformat_to(loc, buffer, format_str, args); return fmt::to_string(buffer);
return fmt::to_string(buffer);
} }
} // namespace internal } // namespace internal
template <typename S, typename Char = char_t<S>> template<typename S, typename Char = char_t<S>>
inline std::basic_string<Char> vformat( inline std::basic_string<Char> vformat(const std::locale &loc, const S &format_str, basic_format_args<buffer_context<Char>> args)
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);
return internal::vformat(loc, to_string_view(format_str), args);
} }
template <typename S, typename... Args, typename Char = char_t<S>> template<typename S, typename... Args, typename Char = char_t<S>>
inline std::basic_string<Char> format(const std::locale& loc, inline std::basic_string<Char> format(const std::locale &loc, const S &format_str, Args &&... args)
const S& format_str, Args&&... args) { {
return internal::vformat( return internal::vformat(loc, to_string_view(format_str), {internal::make_args_checked<Args...>(format_str, args...)});
loc, to_string_view(format_str),
{internal::make_args_checked<Args...>(format_str, args...)});
} }
template <typename S, typename OutputIt, typename... Args, template<typename S, typename OutputIt, typename... Args,
typename Char = enable_if_t< typename Char = enable_if_t<internal::is_output_iterator<OutputIt>::value, char_t<S>>>
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)
inline OutputIt vformat_to(OutputIt out, const std::locale& loc, {
const S& format_str, using range = internal::output_range<OutputIt, Char>;
format_args_t<OutputIt, Char> args) { return vformat_to<arg_formatter<range>>(range(out), to_string_view(format_str), args, internal::locale_ref(loc));
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, template<typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(internal::is_output_iterator<OutputIt>::value&& FMT_ENABLE_IF(internal::is_output_iterator<OutputIt>::value &&internal::is_string<S>::value)>
internal::is_string<S>::value)> inline OutputIt format_to(OutputIt out, const std::locale &loc, const S &format_str, Args &&... args)
inline OutputIt format_to(OutputIt out, const std::locale& loc, {
const S& format_str, Args&&... args) { internal::check_format_string<Args...>(format_str);
internal::check_format_string<Args...>(format_str); using context = format_context_t<OutputIt, char_t<S>>;
using context = format_context_t<OutputIt, char_t<S>>; format_arg_store<context, Args...> as{args...};
format_arg_store<context, Args...> as{args...}; return vformat_to(out, loc, to_string_view(format_str), basic_format_args<context>(as));
return vformat_to(out, loc, to_string_view(format_str),
basic_format_args<context>(as));
} }
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif // FMT_LOCALE_H_ #endif // FMT_LOCALE_H_

View File

@ -14,111 +14,122 @@
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace internal { namespace internal {
template <class Char> class formatbuf : public std::basic_streambuf<Char> { template<class Char>
private: class formatbuf : public std::basic_streambuf<Char>
using int_type = typename std::basic_streambuf<Char>::int_type; {
using traits_type = typename std::basic_streambuf<Char>::traits_type; 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: public:
formatbuf(buffer<Char>& buf) : buffer_(buf) {} formatbuf(buffer<Char> &buf)
: buffer_(buf)
{}
protected: protected:
// The put-area is actually always empty. This makes the implementation // The put-area is actually always empty. This makes the implementation
// simpler and has the advantage that the streambuf and the buffer are always // simpler and has the advantage that the streambuf and the buffer are always
// in sync and sputc never writes into uninitialized memory. The obvious // in sync and sputc never writes into uninitialized memory. The obvious
// disadvantage is that each call to sputc always results in a (virtual) call // 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 // to overflow. There is no disadvantage here for sputn since this always
// results in a call to xsputn. // results in a call to xsputn.
int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE { 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)); if (!traits_type::eq_int_type(ch, traits_type::eof()))
return ch; buffer_.push_back(static_cast<Char>(ch));
} return ch;
}
std::streamsize xsputn(const Char* s, std::streamsize count) FMT_OVERRIDE { std::streamsize xsputn(const Char *s, std::streamsize count) FMT_OVERRIDE
buffer_.append(s, s + count); {
return count; buffer_.append(s, s + count);
} return count;
}
}; };
template <typename Char> struct test_stream : std::basic_ostream<Char> { template<typename Char>
private: struct test_stream : std::basic_ostream<Char>
// Hide all operator<< from std::basic_ostream<Char>. {
void_t<> operator<<(null<>); private:
void_t<> operator<<(const Char*); // 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 && template<typename T, FMT_ENABLE_IF(std::is_convertible<T, int>::value && !std::is_enum<T>::value)>
!std::is_enum<T>::value)> void_t<> operator<<(T);
void_t<> operator<<(T);
}; };
// Checks if T has a user-defined operator<< (e.g. not a member of // Checks if T has a user-defined operator<< (e.g. not a member of
// std::ostream). // std::ostream).
template <typename T, typename Char> class is_streamable { template<typename T, typename Char>
private: class is_streamable
template <typename U> {
static bool_constant<!std::is_same<decltype(std::declval<test_stream<Char>&>() private:
<< std::declval<U>()), template<typename U>
void_t<>>::value> static bool_constant<!std::is_same<decltype(std::declval<test_stream<Char> &>() << std::declval<U>()), void_t<>>::value> test(int);
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: public:
static const bool value = result::value; static const bool value = result::value;
}; };
// Write the content of buf to os. // Write the content of buf to os.
template <typename Char> template<typename Char>
void write(std::basic_ostream<Char>& os, buffer<Char>& buf) { 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; const Char *buf_data = buf.data();
unsigned_streamsize size = buf.size(); using unsigned_streamsize = std::make_unsigned<std::streamsize>::type;
unsigned_streamsize max_size = to_unsigned(max_value<std::streamsize>()); unsigned_streamsize size = buf.size();
do { unsigned_streamsize max_size = to_unsigned(max_value<std::streamsize>());
unsigned_streamsize n = size <= max_size ? size : max_size; do
os.write(buf_data, static_cast<std::streamsize>(n)); {
buf_data += n; unsigned_streamsize n = size <= max_size ? size : max_size;
size -= n; os.write(buf_data, static_cast<std::streamsize>(n));
} while (size != 0); buf_data += n;
size -= n;
} while (size != 0);
} }
template <typename Char, typename T> template<typename Char, typename T>
void format_value(buffer<Char>& buf, const T& value, void format_value(buffer<Char> &buf, const T &value, locale_ref loc = locale_ref())
locale_ref loc = locale_ref()) { {
formatbuf<Char> format_buf(buf); formatbuf<Char> format_buf(buf);
std::basic_ostream<Char> output(&format_buf); std::basic_ostream<Char> output(&format_buf);
if (loc) output.imbue(loc.get<std::locale>()); if (loc)
output.exceptions(std::ios_base::failbit | std::ios_base::badbit); output.imbue(loc.get<std::locale>());
output << value; output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
buf.resize(buf.size()); output << value;
buf.resize(buf.size());
} }
// Formats an object of type T that has an overloaded ostream operator<<. // Formats an object of type T that has an overloaded ostream operator<<.
template <typename T, typename Char> template<typename T, typename Char>
struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>> struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>> : formatter<basic_string_view<Char>, Char>
: formatter<basic_string_view<Char>, Char> { {
template <typename Context> template<typename Context>
auto format(const T& value, Context& ctx) -> decltype(ctx.out()) { auto format(const T &value, Context &ctx) -> decltype(ctx.out())
basic_memory_buffer<Char> buffer; {
format_value(buffer, value, ctx.locale()); basic_memory_buffer<Char> buffer;
basic_string_view<Char> str(buffer.data(), buffer.size()); format_value(buffer, value, ctx.locale());
return formatter<basic_string_view<Char>, Char>::format(str, ctx); 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> template<typename Char>
void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str, void vprint(std::basic_ostream<Char> &os, basic_string_view<Char> format_str, basic_format_args<buffer_context<Char>> args)
basic_format_args<buffer_context<Char>> args) { {
basic_memory_buffer<Char> buffer; basic_memory_buffer<Char> buffer;
internal::vformat_to(buffer, format_str, args); internal::vformat_to(buffer, format_str, args);
internal::write(os, buffer); 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"); fmt::print(cerr, "Don't {}!", "panic");
\endrst \endrst
*/ */
template <typename S, typename... Args, template<typename S, typename... Args, typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
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)
void print(std::basic_ostream<Char>& os, const S& format_str, Args&&... args) { {
vprint(os, to_string_view(format_str), vprint(os, to_string_view(format_str), {internal::make_args_checked<Args...>(format_str, args...)});
{internal::make_args_checked<Args...>(format_str, args...)});
} }
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif // FMT_OSTREAM_H_ #endif // FMT_OSTREAM_H_

View File

@ -10,65 +10,65 @@
#if defined(__MINGW32__) || defined(__CYGWIN__) #if defined(__MINGW32__) || defined(__CYGWIN__)
// Workaround MinGW bug https://sourceforge.net/p/mingw/bugs/2024/. // Workaround MinGW bug https://sourceforge.net/p/mingw/bugs/2024/.
# undef __STRICT_ANSI__ #undef __STRICT_ANSI__
#endif #endif
#include <cerrno> #include <cerrno>
#include <clocale> // for locale_t #include <clocale> // for locale_t
#include <cstdio> #include <cstdio>
#include <cstdlib> // for strtod_l #include <cstdlib> // for strtod_l
#include <cstddef> #include <cstddef>
#if defined __APPLE__ || defined(__FreeBSD__) #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 #endif
#include "format.h" #include "format.h"
// UWP doesn't provide _pipe. // UWP doesn't provide _pipe.
#if FMT_HAS_INCLUDE("winapifamily.h") #if FMT_HAS_INCLUDE("winapifamily.h")
# include <winapifamily.h> #include <winapifamily.h>
#endif #endif
#if FMT_HAS_INCLUDE("fcntl.h") && \ #if FMT_HAS_INCLUDE("fcntl.h") && (!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
(!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)) #include <fcntl.h> // for O_RDONLY
# include <fcntl.h> // for O_RDONLY #define FMT_USE_FCNTL 1
# define FMT_USE_FCNTL 1
#else #else
# define FMT_USE_FCNTL 0 #define FMT_USE_FCNTL 0
#endif #endif
#ifndef FMT_POSIX #ifndef FMT_POSIX
# if defined(_WIN32) && !defined(__MINGW32__) #if defined(_WIN32) && !defined(__MINGW32__)
// Fix warnings about deprecated symbols. // Fix warnings about deprecated symbols.
# define FMT_POSIX(call) _##call #define FMT_POSIX(call) _##call
# else #else
# define FMT_POSIX(call) call #define FMT_POSIX(call) call
# endif #endif
#endif #endif
// Calls to system functions are wrapped in FMT_SYSTEM for testability. // Calls to system functions are wrapped in FMT_SYSTEM for testability.
#ifdef FMT_SYSTEM #ifdef FMT_SYSTEM
# define FMT_POSIX_CALL(call) FMT_SYSTEM(call) #define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
#else #else
# define FMT_SYSTEM(call) call #define FMT_SYSTEM(call) call
# ifdef _WIN32 #ifdef _WIN32
// Fix warnings about deprecated symbols. // Fix warnings about deprecated symbols.
# define FMT_POSIX_CALL(call) ::_##call #define FMT_POSIX_CALL(call) ::_##call
# else #else
# define FMT_POSIX_CALL(call) ::call #define FMT_POSIX_CALL(call) ::call
# endif #endif
#endif #endif
// Retries the expression while it evaluates to error_result and errno // Retries the expression while it evaluates to error_result and errno
// equals to EINTR. // equals to EINTR.
#ifndef _WIN32 #ifndef _WIN32
# define FMT_RETRY_VAL(result, expression, error_result) \ #define FMT_RETRY_VAL(result, expression, error_result) \
do { \ do \
(result) = (expression); \ { \
(result) = (expression); \
} while ((result) == (error_result) && errno == EINTR) } while ((result) == (error_result) && errno == EINTR)
#else #else
# define FMT_RETRY_VAL(result, expression, error_result) result = (expression) #define FMT_RETRY_VAL(result, expression, error_result) result = (expression)
#endif #endif
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1) #define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
@ -100,91 +100,114 @@ FMT_BEGIN_NAMESPACE
format(std::string("{}"), 42); format(std::string("{}"), 42);
\endrst \endrst
*/ */
template <typename Char> class basic_cstring_view { template<typename Char>
private: class basic_cstring_view
const Char* data_; {
private:
const Char *data_;
public: public:
/** Constructs a string reference object from a C string. */ /** Constructs a string reference object from a C string. */
basic_cstring_view(const Char* s) : data_(s) {} basic_cstring_view(const Char *s)
: data_(s)
{}
/** /**
\rst \rst
Constructs a string reference from an ``std::string`` object. Constructs a string reference from an ``std::string`` object.
\endrst \endrst
*/ */
basic_cstring_view(const std::basic_string<Char>& s) : data_(s.c_str()) {} basic_cstring_view(const std::basic_string<Char> &s)
: data_(s.c_str())
{}
/** Returns the pointer to a C string. */ /** Returns the pointer to a C string. */
const Char* c_str() const { return data_; } const Char *c_str() const
{
return data_;
}
}; };
using cstring_view = basic_cstring_view<char>; using cstring_view = basic_cstring_view<char>;
using wcstring_view = basic_cstring_view<wchar_t>; using wcstring_view = basic_cstring_view<wchar_t>;
// An error code. // An error code.
class error_code { class error_code
private: {
int value_; private:
int value_;
public: public:
explicit error_code(int value = 0) FMT_NOEXCEPT : value_(value) {} 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. // A buffered file.
class buffered_file { class buffered_file
private: {
FILE* 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: public:
buffered_file(const buffered_file&) = delete; buffered_file(const buffered_file &) = delete;
void operator=(const buffered_file&) = delete; void operator=(const buffered_file &) = delete;
// Constructs a buffered_file object which doesn't represent any file. // Constructs a buffered_file object which doesn't represent any file.
buffered_file() FMT_NOEXCEPT : file_(nullptr) {} buffered_file() FMT_NOEXCEPT : file_(nullptr) {}
// Destroys the object closing the file it represents if any. // Destroys the object closing the file it represents if any.
FMT_API ~buffered_file() FMT_NOEXCEPT; FMT_API ~buffered_file() FMT_NOEXCEPT;
public: public:
buffered_file(buffered_file&& other) FMT_NOEXCEPT : file_(other.file_) { buffered_file(buffered_file &&other) FMT_NOEXCEPT : file_(other.file_)
other.file_ = nullptr; {
} other.file_ = nullptr;
}
buffered_file& operator=(buffered_file&& other) { buffered_file &operator=(buffered_file &&other)
close(); {
file_ = other.file_; close();
other.file_ = nullptr; file_ = other.file_;
return *this; other.file_ = nullptr;
} return *this;
}
// Opens a file. // Opens a file.
FMT_API buffered_file(cstring_view filename, cstring_view mode); FMT_API buffered_file(cstring_view filename, cstring_view mode);
// Closes the file. // Closes the file.
FMT_API void close(); FMT_API void close();
// Returns the pointer to a FILE object representing this file. // Returns the pointer to a FILE object representing this file.
FILE* get() const FMT_NOEXCEPT { return file_; } FILE *get() const FMT_NOEXCEPT
{
return file_;
}
// We place parentheses around fileno to workaround a bug in some versions // We place parentheses around fileno to workaround a bug in some versions
// of MinGW that define fileno as a macro. // of MinGW that define fileno as a macro.
FMT_API int(fileno)() const; FMT_API int(fileno)() const;
void vprint(string_view format_str, format_args args) { void vprint(string_view format_str, format_args args)
fmt::vprint(file_, format_str, args); {
} fmt::vprint(file_, format_str, args);
}
template <typename... Args> template<typename... Args>
inline void print(string_view format_str, const Args&... args) { inline void print(string_view format_str, const Args &... args)
vprint(format_str, make_format_args(args...)); {
} vprint(format_str, make_format_args(args...));
}
}; };
#if FMT_USE_FCNTL #if FMT_USE_FCNTL
@ -194,128 +217,158 @@ class buffered_file {
// closing the file multiple times will cause a crash on Windows rather // closing the file multiple times will cause a crash on Windows rather
// than an exception. You can get standard behavior by overriding the // than an exception. You can get standard behavior by overriding the
// invalid parameter handler with _set_invalid_parameter_handler. // invalid parameter handler with _set_invalid_parameter_handler.
class file { class file
private: {
int fd_; // File descriptor. private:
int fd_; // File descriptor.
// Constructs a file object with a given descriptor. // Constructs a file object with a given descriptor.
explicit file(int fd) : fd_(fd) {} explicit file(int fd)
: fd_(fd)
{}
public: public:
// Possible values for the oflag argument to the constructor. // Possible values for the oflag argument to the constructor.
enum { enum
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only. {
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only. RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing. 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. // Constructs a file object which doesn't represent any file.
file() FMT_NOEXCEPT : fd_(-1) {} file() FMT_NOEXCEPT : fd_(-1) {}
// Opens a file and constructs a file object representing this file. // Opens a file and constructs a file object representing this file.
FMT_API file(cstring_view path, int oflag); FMT_API file(cstring_view path, int oflag);
public: public:
file(const file&) = delete; file(const file &) = delete;
void operator=(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 { file &operator=(file &&other) FMT_NOEXCEPT
close(); {
fd_ = other.fd_; close();
other.fd_ = -1; fd_ = other.fd_;
return *this; other.fd_ = -1;
} return *this;
}
// Destroys the object closing the file it represents if any. // Destroys the object closing the file it represents if any.
FMT_API ~file() FMT_NOEXCEPT; FMT_API ~file() FMT_NOEXCEPT;
// Returns the file descriptor. // Returns the file descriptor.
int descriptor() const FMT_NOEXCEPT { return fd_; } int descriptor() const FMT_NOEXCEPT
{
return fd_;
}
// Closes the file. // Closes the file.
FMT_API void close(); FMT_API void close();
// Returns the file size. The size has signed type for consistency with // Returns the file size. The size has signed type for consistency with
// stat::st_size. // stat::st_size.
FMT_API long long size() const; FMT_API long long size() const;
// Attempts to read count bytes from the file into the specified buffer. // Attempts to read count bytes from the file into the specified buffer.
FMT_API std::size_t read(void* buffer, std::size_t count); FMT_API std::size_t read(void *buffer, std::size_t count);
// Attempts to write count bytes from the specified buffer to the file. // 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); FMT_API std::size_t write(const void *buffer, std::size_t count);
// Duplicates a file descriptor with the dup function and returns // Duplicates a file descriptor with the dup function and returns
// the duplicate as a file object. // the duplicate as a file object.
FMT_API static file dup(int fd); FMT_API static file dup(int fd);
// Makes fd be the copy of this file descriptor, closing fd first if // Makes fd be the copy of this file descriptor, closing fd first if
// necessary. // necessary.
FMT_API void dup2(int fd); FMT_API void dup2(int fd);
// Makes fd be the copy of this file descriptor, closing fd first if // Makes fd be the copy of this file descriptor, closing fd first if
// necessary. // necessary.
FMT_API void dup2(int fd, error_code& ec) FMT_NOEXCEPT; 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 // Creates a pipe setting up read_end and write_end file objects for reading
// and writing respectively. // and writing respectively.
FMT_API static void pipe(file& read_end, file& write_end); FMT_API static void pipe(file &read_end, file &write_end);
// Creates a buffered_file object associated with this file and detaches // Creates a buffered_file object associated with this file and detaches
// this file object from the file. // this file object from the file.
FMT_API buffered_file fdopen(const char* mode); FMT_API buffered_file fdopen(const char *mode);
}; };
// Returns the memory page size. // Returns the memory page size.
long getpagesize(); long getpagesize();
#endif // FMT_USE_FCNTL #endif // FMT_USE_FCNTL
#ifdef FMT_LOCALE #ifdef FMT_LOCALE
// A "C" numeric locale. // A "C" numeric locale.
class Locale { class Locale
private: {
# ifdef _WIN32 private:
using locale_t = _locale_t; #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) { static locale_t newlocale(int category_mask, const char *locale, locale_t)
return _create_locale(category_mask, locale); {
} 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) { static double strtod_l(const char *nptr, char **endptr, _locale_t locale)
return _strtod_l(nptr, endptr, locale); {
} return _strtod_l(nptr, endptr, locale);
# endif }
#endif
locale_t locale_; locale_t locale_;
public: public:
using type = locale_t; using type = locale_t;
Locale(const Locale&) = delete; Locale(const Locale &) = delete;
void operator=(const Locale&) = delete; void operator=(const Locale &) = delete;
Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", nullptr)) { Locale()
if (!locale_) FMT_THROW(system_error(errno, "cannot create locale")); : locale_(newlocale(LC_NUMERIC_MASK, "C", nullptr))
} {
~Locale() { freelocale(locale_); } 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 // Converts string to floating-point number and advances str past the end
// of the parsed input. // of the parsed input.
double strtod(const char*& str) const { double strtod(const char *&str) const
char* end = nullptr; {
double result = strtod_l(str, &end, locale_); char *end = nullptr;
str = end; double result = strtod_l(str, &end, locale_);
return result; str = end;
} return result;
}
}; };
#endif // FMT_LOCALE #endif // FMT_LOCALE
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif // FMT_POSIX_H_ #endif // FMT_POSIX_H_

File diff suppressed because it is too large Load Diff

View File

@ -17,324 +17,374 @@
// output only up to N items from the range. // output only up to N items from the range.
#ifndef FMT_RANGE_OUTPUT_LENGTH_LIMIT #ifndef FMT_RANGE_OUTPUT_LENGTH_LIMIT
# define FMT_RANGE_OUTPUT_LENGTH_LIMIT 256 #define FMT_RANGE_OUTPUT_LENGTH_LIMIT 256
#endif #endif
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
template <typename Char> struct formatting_base { template<typename Char>
template <typename ParseContext> struct formatting_base
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { {
return ctx.begin(); template<typename ParseContext>
} FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin())
{
return ctx.begin();
}
}; };
template <typename Char, typename Enable = void> template<typename Char, typename Enable = void>
struct formatting_range : formatting_base<Char> { 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 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. // range.
Char prefix; Char prefix;
Char delimiter; Char delimiter;
Char postfix; Char postfix;
formatting_range() : prefix('{'), delimiter(','), postfix('}') {} formatting_range()
static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true; : prefix('{')
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false; , 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> template<typename Char, typename Enable = void>
struct formatting_tuple : formatting_base<Char> { struct formatting_tuple : formatting_base<Char>
Char prefix; {
Char delimiter; Char prefix;
Char postfix; Char delimiter;
formatting_tuple() : prefix('('), delimiter(','), postfix(')') {} Char postfix;
static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true; formatting_tuple()
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false; : 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 { namespace internal {
template <typename RangeT, typename OutputIterator> template<typename RangeT, typename OutputIterator>
OutputIterator copy(const RangeT& range, OutputIterator out) { OutputIterator copy(const RangeT &range, OutputIterator out)
for (auto it = range.begin(), end = range.end(); it != end; ++it) {
*out++ = *it; for (auto it = range.begin(), end = range.end(); it != end; ++it)
return out; *out++ = *it;
return out;
} }
template <typename OutputIterator> template<typename OutputIterator>
OutputIterator copy(const char* str, OutputIterator out) { OutputIterator copy(const char *str, OutputIterator out)
while (*str) *out++ = *str++; {
return out; while (*str)
*out++ = *str++;
return out;
} }
template <typename OutputIterator> template<typename OutputIterator>
OutputIterator copy(char ch, OutputIterator out) { OutputIterator copy(char ch, OutputIterator out)
*out++ = ch; {
return out; *out++ = ch;
return out;
} }
/// Return true value if T has std::string interface, like std::string_view. /// Return true value if T has std::string interface, like std::string_view.
template <typename T> class is_like_std_string { template<typename T>
template <typename U> class is_like_std_string
static auto check(U* p) {
-> decltype((void)p->find('a'), p->length(), (void)p->data(), int()); template<typename U>
template <typename> static void check(...); static auto check(U *p) -> decltype((void)p->find('a'), p->length(), (void)p->data(), int());
template<typename>
static void check(...);
public: public:
static FMT_CONSTEXPR_DECL const bool value = static FMT_CONSTEXPR_DECL const bool value = is_string<T>::value || !std::is_void<decltype(check<T>(nullptr))>::value;
is_string<T>::value || !std::is_void<decltype(check<T>(nullptr))>::value;
}; };
template <typename Char> template<typename Char>
struct is_like_std_string<fmt::basic_string_view<Char>> : std::true_type {}; 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 #if !FMT_MSC_VER || FMT_MSC_VER > 1800
template <typename T> template<typename T>
struct is_range_< struct is_range_<T, conditional_t<false, conditional_helper<decltype(std::declval<T>().begin()), decltype(std::declval<T>().end())>, void>>
T, conditional_t<false, : std::true_type
conditional_helper<decltype(std::declval<T>().begin()), {};
decltype(std::declval<T>().end())>,
void>> : std::true_type {};
#endif #endif
/// tuple_size and tuple_element check. /// tuple_size and tuple_element check.
template <typename T> class is_tuple_like_ { template<typename T>
template <typename U> class is_tuple_like_
static auto check(U* p) {
-> decltype(std::tuple_size<U>::value, template<typename U>
(void)std::declval<typename std::tuple_element<0, U>::type>(), static auto check(U *p) -> decltype(std::tuple_size<U>::value, (void)std::declval<typename std::tuple_element<0, U>::type>(), int());
int()); template<typename>
template <typename> static void check(...); static void check(...);
public: public:
static FMT_CONSTEXPR_DECL const bool value = static FMT_CONSTEXPR_DECL const bool value = !std::is_void<decltype(check<T>(nullptr))>::value;
!std::is_void<decltype(check<T>(nullptr))>::value;
}; };
// Check for integer_sequence // Check for integer_sequence
#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1900 #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...>; 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>; using make_index_sequence = std::make_index_sequence<N>;
#else #else
template <typename T, T... N> struct integer_sequence { template<typename T, T... N>
using value_type = T; 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...>; using index_sequence = integer_sequence<std::size_t, N...>;
template <typename T, std::size_t N, T... Ns> template<typename T, std::size_t N, T... Ns>
struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, 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, 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>; using make_index_sequence = make_integer_sequence<std::size_t, N>;
#endif #endif
template <class Tuple, class F, size_t... Is> template<class Tuple, class F, size_t... Is>
void for_each(index_sequence<Is...>, Tuple&& tup, F&& f) FMT_NOEXCEPT { void for_each(index_sequence<Is...>, Tuple &&tup, F &&f) FMT_NOEXCEPT
using std::get; {
// using free function get<I>(T) now. using std::get;
const int _[] = {0, ((void)f(get<Is>(tup)), 0)...}; // using free function get<I>(T) now.
(void)_; // blocks warnings const int _[] = {0, ((void)f(get<Is>(tup)), 0)...};
(void)_; // blocks warnings
} }
template <class T> template<class T>
FMT_CONSTEXPR make_index_sequence<std::tuple_size<T>::value> get_indexes( FMT_CONSTEXPR make_index_sequence<std::tuple_size<T>::value> get_indexes(T const &)
T const&) { {
return {}; return {};
} }
template <class Tuple, class F> void for_each(Tuple&& tup, F&& f) { template<class Tuple, class F>
const auto indexes = get_indexes(tup); void for_each(Tuple &&tup, F &&f)
for_each(indexes, std::forward<Tuple>(tup), std::forward<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< template<typename Arg, FMT_ENABLE_IF(!is_like_std_string<typename std::decay<Arg>::type>::value)>
typename std::decay<Arg>::type>::value)> FMT_CONSTEXPR const char *format_str_quoted(bool add_space, const Arg &)
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) { {
return add_space ? " {}" : "{}"; return add_space ? " {}" : "{}";
} }
template <typename Arg, FMT_ENABLE_IF(is_like_std_string< template<typename Arg, FMT_ENABLE_IF(is_like_std_string<typename std::decay<Arg>::type>::value)>
typename std::decay<Arg>::type>::value)> FMT_CONSTEXPR const char *format_str_quoted(bool add_space, const Arg &)
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) { {
return add_space ? " \"{}\"" : "\"{}\""; return add_space ? " \"{}\"" : "\"{}\"";
} }
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char*) { FMT_CONSTEXPR const char *format_str_quoted(bool add_space, const char *)
return add_space ? " \"{}\"" : "\"{}\""; {
return add_space ? " \"{}\"" : "\"{}\"";
} }
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t*) { FMT_CONSTEXPR const wchar_t *format_str_quoted(bool add_space, const wchar_t *)
return add_space ? L" \"{}\"" : L"\"{}\""; {
return add_space ? L" \"{}\"" : L"\"{}\"";
} }
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char) { FMT_CONSTEXPR const char *format_str_quoted(bool add_space, const char)
return add_space ? " '{}'" : "'{}'"; {
return add_space ? " '{}'" : "'{}'";
} }
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t) { FMT_CONSTEXPR const wchar_t *format_str_quoted(bool add_space, const wchar_t)
return add_space ? L" '{}'" : L"'{}'"; {
return add_space ? L" '{}'" : L"'{}'";
} }
} // namespace internal } // namespace internal
template <typename T> struct is_tuple_like { template<typename T>
static FMT_CONSTEXPR_DECL const bool value = struct is_tuple_like
internal::is_tuple_like_<T>::value && !internal::is_range_<T>::value; {
static FMT_CONSTEXPR_DECL const bool value = internal::is_tuple_like_<T>::value && !internal::is_range_<T>::value;
}; };
template <typename TupleT, typename Char> template<typename TupleT, typename Char>
struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> { struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>>
private: {
// C++11 generic lambda for format() private:
template <typename FormatContext> struct format_each { // C++11 generic lambda for format()
template <typename T> void operator()(const T& v) { template<typename FormatContext>
if (i > 0) { struct format_each
if (formatting.add_prepostfix_space) { {
*out++ = ' '; 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);
} formatting_tuple<Char> &formatting;
out = format_to(out, std::size_t &i;
internal::format_str_quoted( typename std::add_lvalue_reference<decltype(std::declval<FormatContext>().out())>::type out;
(formatting.add_delimiter_spaces && i > 0), v), };
v);
++i; 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; template<typename FormatContext = format_context>
std::size_t& i; auto format(const TupleT &values, FormatContext &ctx) -> decltype(ctx.out())
typename std::add_lvalue_reference<decltype( {
std::declval<FormatContext>().out())>::type out; auto out = ctx.out();
}; std::size_t i = 0;
internal::copy(formatting.prefix, out);
public: internal::for_each(values, format_each<FormatContext>{formatting, i, out});
formatting_tuple<Char> formatting; if (formatting.add_prepostfix_space)
{
*out++ = ' ';
}
internal::copy(formatting.postfix, out);
template <typename ParseContext> return ctx.out();
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return formatting.parse(ctx);
}
template <typename FormatContext = format_context>
auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) {
auto out = ctx.out();
std::size_t i = 0;
internal::copy(formatting.prefix, out);
internal::for_each(values, format_each<FormatContext>{formatting, i, out});
if (formatting.add_prepostfix_space) {
*out++ = ' ';
} }
internal::copy(formatting.postfix, out);
return ctx.out();
}
}; };
template <typename T, typename Char> struct is_range { template<typename T, typename Char>
static FMT_CONSTEXPR_DECL const bool value = struct is_range
internal::is_range_<T>::value && {
!internal::is_like_std_string<T>::value && static FMT_CONSTEXPR_DECL const bool value = internal::is_range_<T>::value && !internal::is_like_std_string<T>::value &&
!std::is_convertible<T, std::basic_string<Char>>::value && !std::is_convertible<T, std::basic_string<Char>>::value &&
!std::is_constructible<internal::std_string_view<Char>, T>::value; !std::is_constructible<internal::std_string_view<Char>, T>::value;
}; };
template <typename RangeT, typename Char> template<typename RangeT, typename Char>
struct formatter<RangeT, Char, struct formatter<RangeT, Char, enable_if_t<fmt::is_range<RangeT, Char>::value>>
enable_if_t<fmt::is_range<RangeT, Char>::value>> { {
formatting_range<Char> formatting; formatting_range<Char> formatting;
template <typename ParseContext> template<typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin())
return formatting.parse(ctx); {
} return formatting.parse(ctx);
template <typename FormatContext>
typename FormatContext::iterator format(const RangeT& values,
FormatContext& ctx) {
auto out = internal::copy(formatting.prefix, ctx.out());
std::size_t i = 0;
for (auto it = values.begin(), end = values.end(); it != end; ++it) {
if (i > 0) {
if (formatting.add_prepostfix_space) *out++ = ' ';
out = internal::copy(formatting.delimiter, out);
}
out = format_to(out,
internal::format_str_quoted(
(formatting.add_delimiter_spaces && i > 0), *it),
*it);
if (++i > formatting.range_length_limit) {
out = format_to(out, " ... <other elements>");
break;
}
} }
if (formatting.add_prepostfix_space) *out++ = ' ';
return internal::copy(formatting.postfix, out);
}
};
template <typename Char, typename... T> struct tuple_arg_join : internal::view { template<typename FormatContext>
const std::tuple<T...>& tuple; typename FormatContext::iterator format(const RangeT &values, FormatContext &ctx)
basic_string_view<Char> sep; {
auto out = internal::copy(formatting.prefix, ctx.out());
tuple_arg_join(const std::tuple<T...>& t, basic_string_view<Char> s) std::size_t i = 0;
: tuple{t}, sep{s} {} for (auto it = values.begin(), end = values.end(); it != end; ++it)
}; {
if (i > 0)
template <typename Char, typename... T> {
struct formatter<tuple_arg_join<Char, T...>, Char> { if (formatting.add_prepostfix_space)
template <typename ParseContext> *out++ = ' ';
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { out = internal::copy(formatting.delimiter, out);
return ctx.begin(); }
} out = format_to(out, internal::format_str_quoted((formatting.add_delimiter_spaces && i > 0), *it), *it);
if (++i > formatting.range_length_limit)
template <typename FormatContext> {
typename FormatContext::iterator format( out = format_to(out, " ... <other elements>");
const tuple_arg_join<Char, T...>& value, FormatContext& ctx) { break;
return format(value, ctx, internal::make_index_sequence<sizeof...(T)>{}); }
} }
if (formatting.add_prepostfix_space)
private: *out++ = ' ';
template <typename FormatContext, size_t... N> return internal::copy(formatting.postfix, out);
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 Char, typename... T>
} struct tuple_arg_join : internal::view
{
template <typename FormatContext> const std::tuple<T...> &tuple;
typename FormatContext::iterator format_args( basic_string_view<Char> sep;
const tuple_arg_join<Char, T...>&, FormatContext& ctx) {
// NOTE: for compilers that support C++17, this empty function instantiation tuple_arg_join(const std::tuple<T...> &t, basic_string_view<Char> s)
// can be replaced with a constexpr branch in the variadic overload. : tuple{t}
return ctx.out(); , sep{s}
} {}
};
template <typename FormatContext, typename Arg, typename... Args>
typename FormatContext::iterator format_args( template<typename Char, typename... T>
const tuple_arg_join<Char, T...>& value, FormatContext& ctx, struct formatter<tuple_arg_join<Char, T...>, Char>
const Arg& arg, const Args&... args) { {
using base = formatter<typename std::decay<Arg>::type, Char>; template<typename ParseContext>
auto out = ctx.out(); FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin())
out = base{}.format(arg, ctx); {
if (sizeof...(Args) > 0) { return ctx.begin();
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 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" // Output: "1, a"
\endrst \endrst
*/ */
template <typename... T> template<typename... T>
FMT_CONSTEXPR tuple_arg_join<char, T...> join(const std::tuple<T...>& tuple, FMT_CONSTEXPR tuple_arg_join<char, T...> join(const std::tuple<T...> &tuple, string_view sep)
string_view sep) { {
return {tuple, sep}; return {tuple, sep};
} }
template <typename... T> template<typename... T>
FMT_CONSTEXPR tuple_arg_join<wchar_t, T...> join(const std::tuple<T...>& tuple, FMT_CONSTEXPR tuple_arg_join<wchar_t, T...> join(const std::tuple<T...> &tuple, wstring_view sep)
wstring_view sep) { {
return {tuple, sep}; return {tuple, sep};
} }
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif // FMT_RANGES_H_ #endif // FMT_RANGES_H_