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,7 +74,8 @@ 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(
[&statsdClient, &tokens, &gauge, &timer](const Json::Value& msg,
const std::string& /*position*/, const std::string& /*position*/,
const bool verbose, const bool verbose,
std::atomic<bool>& /*throttled*/, std::atomic<bool>& /*throttled*/,

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,11 +79,13 @@ namespace ix
CobraBot bot; CobraBot bot;
auto jsonWriter = makeStreamWriter(); auto jsonWriter = makeStreamWriter();
bot.setOnBotMessageCallback([&fluentd, &quiet, &jsonWriter](const Json::Value& msg, bot.setOnBotMessageCallback(
[&fluentd, &quiet, &jsonWriter](const Json::Value& msg,
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 {
if (!quiet) if (!quiet)
{ {
writeToStdout(fluentd, jsonWriter, msg, position); writeToStdout(fluentd, jsonWriter, msg, position);

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,
const std::string& prefix)
: _host(host) : _host(host)
, _port(port) , _port(port)
, _prefix(prefix) , _prefix(prefix)
, _stop(false) , _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,17 +117,13 @@ 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); CobraConnection::invokeTrafficTrackerCallback(msg->wireSize, true);
std::stringstream ss; std::stringstream ss;
if (msg->type == ix::WebSocketMessageType::Open) if (msg->type == ix::WebSocketMessageType::Open)
{ {
invokeEventCallback(ix::CobraEventType::Open, invokeEventCallback(ix::CobraEventType::Open, std::string(), msg->openInfo.headers);
std::string(),
msg->openInfo.headers);
sendHandshakeMessage(); sendHandshakeMessage();
} }
else if (msg->type == ix::WebSocketMessageType::Close) else if (msg->type == ix::WebSocketMessageType::Close)
@ -141,8 +133,7 @@ namespace ix
std::stringstream ss; std::stringstream ss;
ss << "Close code " << msg->closeInfo.code; ss << "Close code " << msg->closeInfo.code;
ss << " reason " << msg->closeInfo.reason; ss << " reason " << msg->closeInfo.reason;
invokeEventCallback(ix::CobraEventType::Closed, invokeEventCallback(ix::CobraEventType::Closed, ss.str());
ss.str());
} }
else if (msg->type == ix::WebSocketMessageType::Message) else if (msg->type == ix::WebSocketMessageType::Message)
{ {
@ -166,13 +157,13 @@ namespace ix
{ {
if (!handleHandshakeResponse(data)) if (!handleHandshakeResponse(data))
{ {
invokeErrorCallback("Error extracting nonce from handshake response", msg->str); invokeErrorCallback("Error extracting nonce from handshake response",
msg->str);
} }
} }
else if (action == "auth/handshake/error") else if (action == "auth/handshake/error")
{ {
invokeEventCallback(ix::CobraEventType::HandshakeError, invokeEventCallback(ix::CobraEventType::HandshakeError, msg->str);
msg->str);
} }
else if (action == "auth/authenticate/ok") else if (action == "auth/authenticate/ok")
{ {
@ -182,8 +173,7 @@ namespace ix
} }
else if (action == "auth/authenticate/error") else if (action == "auth/authenticate/error")
{ {
invokeEventCallback(ix::CobraEventType::AuthenticationError, invokeEventCallback(ix::CobraEventType::AuthenticationError, msg->str);
msg->str);
} }
else if (action == "rtm/subscription/data") else if (action == "rtm/subscription/data")
{ {
@ -198,8 +188,7 @@ namespace ix
} }
else if (action == "rtm/subscribe/error") else if (action == "rtm/subscribe/error")
{ {
invokeEventCallback(ix::CobraEventType::SubscriptionError, invokeEventCallback(ix::CobraEventType::SubscriptionError, msg->str);
msg->str);
} }
else if (action == "rtm/unsubscribe/ok") else if (action == "rtm/unsubscribe/ok")
{ {
@ -254,7 +243,8 @@ namespace ix
return _publishMode; return _publishMode;
} }
void CobraConnection::configure(const std::string& appkey, void CobraConnection::configure(
const std::string& appkey,
const std::string& endpoint, const std::string& endpoint,
const std::string& rolename, const std::string& rolename,
const std::string& rolesecret, const std::string& 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,8 +120,7 @@ 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);

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);
@ -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,8 +162,7 @@ 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)
{ {

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,25 +5,24 @@
*/ */
#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; std::stringstream ss;
if (event->type == ix::CobraEventType::Open) if (event->type == ix::CobraEventType::Open)
@ -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,8 +29,7 @@
namespace ix namespace ix
{ {
static const std::string base64_chars = static const std::string base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz" "abcdefghijklmnopqrstuvwxyz"
"0123456789+/"; "0123456789+/";
@ -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,10 +16,10 @@
#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
@ -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,19 +246,16 @@ 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,
@ -283,13 +280,13 @@ 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)
{ {

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,16 +28,14 @@ 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);

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,8 +249,7 @@ 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;
@ -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,8 +143,7 @@ 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
@ -190,8 +188,7 @@ 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;
@ -229,8 +226,7 @@ namespace ix
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,8 +246,7 @@ 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

View File

@ -12,7 +12,8 @@
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
enum class color : uint32_t { enum class color : uint32_t
{
alice_blue = 0xF0F8FF, // rgb(240,248,255) alice_blue = 0xF0F8FF, // rgb(240,248,255)
antique_white = 0xFAEBD7, // rgb(250,235,215) antique_white = 0xFAEBD7, // rgb(250,235,215)
aqua = 0x00FFFF, // rgb(0,255,255) aqua = 0x00FFFF, // rgb(0,255,255)
@ -156,7 +157,8 @@ enum class color : uint32_t {
yellow_green = 0x9ACD32 // rgb(154,205,50) yellow_green = 0x9ACD32 // rgb(154,205,50)
}; // enum class color }; // enum class color
enum class terminal_color : uint8_t { enum class terminal_color : uint8_t
{
black = 30, black = 30,
red, red,
green, green,
@ -175,7 +177,8 @@ enum class terminal_color : uint8_t {
bright_white bright_white
}; };
enum class emphasis : uint8_t { enum class emphasis : uint8_t
{
bold = 1, bold = 1,
italic = 1 << 1, italic = 1 << 1,
underline = 1 << 2, underline = 1 << 2,
@ -184,15 +187,28 @@ enum class emphasis : uint8_t {
// rgb is a struct for red, green and blue colors. // rgb is a struct for red, green and blue colors.
// Using the name "rgb" makes some editors show the color in a tooltip. // Using the name "rgb" makes some editors show the color in a tooltip.
struct rgb { struct rgb
FMT_CONSTEXPR rgb() : r(0), g(0), b(0) {} {
FMT_CONSTEXPR rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {} FMT_CONSTEXPR rgb()
: r(0)
, g(0)
, b(0)
{}
FMT_CONSTEXPR rgb(uint8_t r_, uint8_t g_, uint8_t b_)
: r(r_)
, g(g_)
, b(b_)
{}
FMT_CONSTEXPR rgb(uint32_t hex) FMT_CONSTEXPR rgb(uint32_t hex)
: r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b(hex & 0xFF) {} : r((hex >> 16) & 0xFF)
, g((hex >> 8) & 0xFF)
, b(hex & 0xFF)
{}
FMT_CONSTEXPR rgb(color hex) FMT_CONSTEXPR rgb(color hex)
: r((uint32_t(hex) >> 16) & 0xFF), : r((uint32_t(hex) >> 16) & 0xFF)
g((uint32_t(hex) >> 8) & 0xFF), , g((uint32_t(hex) >> 8) & 0xFF)
b(uint32_t(hex) & 0xFF) {} , b(uint32_t(hex) & 0xFF)
{}
uint8_t r; uint8_t r;
uint8_t g; uint8_t g;
uint8_t b; uint8_t b;
@ -201,22 +217,24 @@ struct rgb {
namespace internal { namespace internal {
// color is a struct of either a rgb color or a terminal color. // color is a struct of either a rgb color or a terminal color.
struct color_type { struct color_type
{
FMT_CONSTEXPR color_type() FMT_NOEXCEPT : is_rgb(), value{} {} FMT_CONSTEXPR color_type() FMT_NOEXCEPT : is_rgb(), value{} {}
FMT_CONSTEXPR color_type(color rgb_color) FMT_NOEXCEPT : is_rgb(true), FMT_CONSTEXPR color_type(color rgb_color) FMT_NOEXCEPT : is_rgb(true), value{}
value{} { {
value.rgb_color = static_cast<uint32_t>(rgb_color); value.rgb_color = static_cast<uint32_t>(rgb_color);
} }
FMT_CONSTEXPR color_type(rgb rgb_color) FMT_NOEXCEPT : is_rgb(true), value{} { FMT_CONSTEXPR color_type(rgb rgb_color) FMT_NOEXCEPT : is_rgb(true), value{}
value.rgb_color = (static_cast<uint32_t>(rgb_color.r) << 16) | {
(static_cast<uint32_t>(rgb_color.g) << 8) | rgb_color.b; value.rgb_color = (static_cast<uint32_t>(rgb_color.r) << 16) | (static_cast<uint32_t>(rgb_color.g) << 8) | rgb_color.b;
} }
FMT_CONSTEXPR color_type(terminal_color term_color) FMT_NOEXCEPT : is_rgb(), FMT_CONSTEXPR color_type(terminal_color term_color) FMT_NOEXCEPT : is_rgb(), value{}
value{} { {
value.term_color = static_cast<uint8_t>(term_color); value.term_color = static_cast<uint8_t>(term_color);
} }
bool is_rgb; bool is_rgb;
union color_union { union color_union
{
uint8_t term_color; uint8_t term_color;
uint32_t rgb_color; uint32_t rgb_color;
} value; } value;
@ -224,112 +242,128 @@ struct color_type {
} // namespace internal } // namespace internal
// Experimental text formatting support. // Experimental text formatting support.
class text_style { class text_style
public: {
FMT_CONSTEXPR text_style(emphasis em = emphasis()) FMT_NOEXCEPT public:
: set_foreground_color(), FMT_CONSTEXPR text_style(emphasis em = emphasis()) FMT_NOEXCEPT : set_foreground_color(), set_background_color(), ems(em) {}
set_background_color(),
ems(em) {}
FMT_CONSTEXPR text_style& operator|=(const text_style& rhs) { FMT_CONSTEXPR text_style &operator|=(const text_style &rhs)
if (!set_foreground_color) { {
if (!set_foreground_color)
{
set_foreground_color = rhs.set_foreground_color; set_foreground_color = rhs.set_foreground_color;
foreground_color = rhs.foreground_color; foreground_color = rhs.foreground_color;
} else if (rhs.set_foreground_color) { }
else if (rhs.set_foreground_color)
{
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb) if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
FMT_THROW(format_error("can't OR a terminal color")); FMT_THROW(format_error("can't OR a terminal color"));
foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color; foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color;
} }
if (!set_background_color) { if (!set_background_color)
{
set_background_color = rhs.set_background_color; set_background_color = rhs.set_background_color;
background_color = rhs.background_color; background_color = rhs.background_color;
} else if (rhs.set_background_color) { }
else if (rhs.set_background_color)
{
if (!background_color.is_rgb || !rhs.background_color.is_rgb) if (!background_color.is_rgb || !rhs.background_color.is_rgb)
FMT_THROW(format_error("can't OR a terminal color")); FMT_THROW(format_error("can't OR a terminal color"));
background_color.value.rgb_color |= rhs.background_color.value.rgb_color; background_color.value.rgb_color |= rhs.background_color.value.rgb_color;
} }
ems = static_cast<emphasis>(static_cast<uint8_t>(ems) | ems = static_cast<emphasis>(static_cast<uint8_t>(ems) | static_cast<uint8_t>(rhs.ems));
static_cast<uint8_t>(rhs.ems));
return *this; return *this;
} }
friend FMT_CONSTEXPR text_style operator|(text_style lhs, friend FMT_CONSTEXPR text_style operator|(text_style lhs, const text_style &rhs)
const text_style& rhs) { {
return lhs |= rhs; return lhs |= rhs;
} }
FMT_CONSTEXPR text_style& operator&=(const text_style& rhs) { FMT_CONSTEXPR text_style &operator&=(const text_style &rhs)
if (!set_foreground_color) { {
if (!set_foreground_color)
{
set_foreground_color = rhs.set_foreground_color; set_foreground_color = rhs.set_foreground_color;
foreground_color = rhs.foreground_color; foreground_color = rhs.foreground_color;
} else if (rhs.set_foreground_color) { }
else if (rhs.set_foreground_color)
{
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb) if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
FMT_THROW(format_error("can't AND a terminal color")); FMT_THROW(format_error("can't AND a terminal color"));
foreground_color.value.rgb_color &= rhs.foreground_color.value.rgb_color; foreground_color.value.rgb_color &= rhs.foreground_color.value.rgb_color;
} }
if (!set_background_color) { if (!set_background_color)
{
set_background_color = rhs.set_background_color; set_background_color = rhs.set_background_color;
background_color = rhs.background_color; background_color = rhs.background_color;
} else if (rhs.set_background_color) { }
else if (rhs.set_background_color)
{
if (!background_color.is_rgb || !rhs.background_color.is_rgb) if (!background_color.is_rgb || !rhs.background_color.is_rgb)
FMT_THROW(format_error("can't AND a terminal color")); FMT_THROW(format_error("can't AND a terminal color"));
background_color.value.rgb_color &= rhs.background_color.value.rgb_color; background_color.value.rgb_color &= rhs.background_color.value.rgb_color;
} }
ems = static_cast<emphasis>(static_cast<uint8_t>(ems) & ems = static_cast<emphasis>(static_cast<uint8_t>(ems) & static_cast<uint8_t>(rhs.ems));
static_cast<uint8_t>(rhs.ems));
return *this; return *this;
} }
friend FMT_CONSTEXPR text_style operator&(text_style lhs, friend FMT_CONSTEXPR text_style operator&(text_style lhs, const text_style &rhs)
const text_style& rhs) { {
return lhs &= rhs; return lhs &= rhs;
} }
FMT_CONSTEXPR bool has_foreground() const FMT_NOEXCEPT { FMT_CONSTEXPR bool has_foreground() const FMT_NOEXCEPT
{
return set_foreground_color; return set_foreground_color;
} }
FMT_CONSTEXPR bool has_background() const FMT_NOEXCEPT { FMT_CONSTEXPR bool has_background() const FMT_NOEXCEPT
{
return set_background_color; return set_background_color;
} }
FMT_CONSTEXPR bool has_emphasis() const FMT_NOEXCEPT { FMT_CONSTEXPR bool has_emphasis() const FMT_NOEXCEPT
{
return static_cast<uint8_t>(ems) != 0; return static_cast<uint8_t>(ems) != 0;
} }
FMT_CONSTEXPR internal::color_type get_foreground() const FMT_NOEXCEPT { FMT_CONSTEXPR internal::color_type get_foreground() const FMT_NOEXCEPT
{
FMT_ASSERT(has_foreground(), "no foreground specified for this style"); FMT_ASSERT(has_foreground(), "no foreground specified for this style");
return foreground_color; return foreground_color;
} }
FMT_CONSTEXPR internal::color_type get_background() const FMT_NOEXCEPT { FMT_CONSTEXPR internal::color_type get_background() const FMT_NOEXCEPT
{
FMT_ASSERT(has_background(), "no background specified for this style"); FMT_ASSERT(has_background(), "no background specified for this style");
return background_color; return background_color;
} }
FMT_CONSTEXPR emphasis get_emphasis() const FMT_NOEXCEPT { FMT_CONSTEXPR emphasis get_emphasis() const FMT_NOEXCEPT
{
FMT_ASSERT(has_emphasis(), "no emphasis specified for this style"); FMT_ASSERT(has_emphasis(), "no emphasis specified for this style");
return ems; return ems;
} }
private: private:
FMT_CONSTEXPR text_style(bool is_foreground, FMT_CONSTEXPR text_style(bool is_foreground, internal::color_type text_color) FMT_NOEXCEPT : set_foreground_color(),
internal::color_type text_color) FMT_NOEXCEPT
: set_foreground_color(),
set_background_color(), set_background_color(),
ems() { ems()
if (is_foreground) { {
if (is_foreground)
{
foreground_color = text_color; foreground_color = text_color;
set_foreground_color = true; set_foreground_color = true;
} else { }
else
{
background_color = text_color; background_color = text_color;
set_background_color = true; set_background_color = true;
} }
} }
friend FMT_CONSTEXPR_DECL text_style fg(internal::color_type foreground) friend FMT_CONSTEXPR_DECL text_style fg(internal::color_type foreground) FMT_NOEXCEPT;
FMT_NOEXCEPT; friend FMT_CONSTEXPR_DECL text_style bg(internal::color_type background) FMT_NOEXCEPT;
friend FMT_CONSTEXPR_DECL text_style bg(internal::color_type background)
FMT_NOEXCEPT;
internal::color_type foreground_color; internal::color_type foreground_color;
internal::color_type background_color; internal::color_type background_color;
@ -338,37 +372,45 @@ class text_style {
emphasis ems; emphasis ems;
}; };
FMT_CONSTEXPR text_style fg(internal::color_type foreground) FMT_NOEXCEPT { FMT_CONSTEXPR text_style fg(internal::color_type foreground) FMT_NOEXCEPT
{
return text_style(/*is_foreground=*/true, foreground); return text_style(/*is_foreground=*/true, foreground);
} }
FMT_CONSTEXPR text_style bg(internal::color_type background) FMT_NOEXCEPT { FMT_CONSTEXPR text_style bg(internal::color_type background) FMT_NOEXCEPT
{
return text_style(/*is_foreground=*/false, background); return text_style(/*is_foreground=*/false, background);
} }
FMT_CONSTEXPR text_style operator|(emphasis lhs, emphasis rhs) FMT_NOEXCEPT { FMT_CONSTEXPR text_style operator|(emphasis lhs, emphasis rhs) FMT_NOEXCEPT
{
return text_style(lhs) | rhs; return text_style(lhs) | rhs;
} }
namespace internal { namespace internal {
template <typename Char> struct ansi_color_escape { template<typename Char>
FMT_CONSTEXPR ansi_color_escape(internal::color_type text_color, struct ansi_color_escape
const char* esc) FMT_NOEXCEPT { {
FMT_CONSTEXPR ansi_color_escape(internal::color_type text_color, const char *esc) FMT_NOEXCEPT
{
// If we have a terminal color, we need to output another escape code // If we have a terminal color, we need to output another escape code
// sequence. // sequence.
if (!text_color.is_rgb) { if (!text_color.is_rgb)
{
bool is_background = esc == internal::data::background_color; bool is_background = esc == internal::data::background_color;
uint32_t value = text_color.value.term_color; uint32_t value = text_color.value.term_color;
// Background ASCII codes are the same as the foreground ones but with // Background ASCII codes are the same as the foreground ones but with
// 10 more. // 10 more.
if (is_background) value += 10u; if (is_background)
value += 10u;
std::size_t index = 0; std::size_t index = 0;
buffer[index++] = static_cast<Char>('\x1b'); buffer[index++] = static_cast<Char>('\x1b');
buffer[index++] = static_cast<Char>('['); buffer[index++] = static_cast<Char>('[');
if (value >= 100u) { if (value >= 100u)
{
buffer[index++] = static_cast<Char>('1'); buffer[index++] = static_cast<Char>('1');
value %= 100u; value %= 100u;
} }
@ -380,7 +422,8 @@ template <typename Char> struct ansi_color_escape {
return; return;
} }
for (int i = 0; i < 7; i++) { for (int i = 0; i < 7; i++)
{
buffer[i] = static_cast<Char>(esc[i]); buffer[i] = static_cast<Char>(esc[i]);
} }
rgb color(text_color.value.rgb_color); rgb color(text_color.value.rgb_color);
@ -389,18 +432,24 @@ template <typename Char> struct ansi_color_escape {
to_esc(color.b, buffer + 15, 'm'); to_esc(color.b, buffer + 15, 'm');
buffer[19] = static_cast<Char>(0); buffer[19] = static_cast<Char>(0);
} }
FMT_CONSTEXPR ansi_color_escape(emphasis em) FMT_NOEXCEPT { FMT_CONSTEXPR ansi_color_escape(emphasis em) FMT_NOEXCEPT
{
uint8_t em_codes[4] = {}; uint8_t em_codes[4] = {};
uint8_t em_bits = static_cast<uint8_t>(em); uint8_t em_bits = static_cast<uint8_t>(em);
if (em_bits & static_cast<uint8_t>(emphasis::bold)) em_codes[0] = 1; if (em_bits & static_cast<uint8_t>(emphasis::bold))
if (em_bits & static_cast<uint8_t>(emphasis::italic)) em_codes[1] = 3; em_codes[0] = 1;
if (em_bits & static_cast<uint8_t>(emphasis::underline)) em_codes[2] = 4; if (em_bits & static_cast<uint8_t>(emphasis::italic))
em_codes[1] = 3;
if (em_bits & static_cast<uint8_t>(emphasis::underline))
em_codes[2] = 4;
if (em_bits & static_cast<uint8_t>(emphasis::strikethrough)) if (em_bits & static_cast<uint8_t>(emphasis::strikethrough))
em_codes[3] = 9; em_codes[3] = 9;
std::size_t index = 0; std::size_t index = 0;
for (int i = 0; i < 4; ++i) { for (int i = 0; i < 4; ++i)
if (!em_codes[i]) continue; {
if (!em_codes[i])
continue;
buffer[index++] = static_cast<Char>('\x1b'); buffer[index++] = static_cast<Char>('\x1b');
buffer[index++] = static_cast<Char>('['); buffer[index++] = static_cast<Char>('[');
buffer[index++] = static_cast<Char>('0' + em_codes[i]); buffer[index++] = static_cast<Char>('0' + em_codes[i]);
@ -408,18 +457,25 @@ template <typename Char> struct ansi_color_escape {
} }
buffer[index++] = static_cast<Char>(0); buffer[index++] = static_cast<Char>(0);
} }
FMT_CONSTEXPR operator const Char*() const FMT_NOEXCEPT { return buffer; } FMT_CONSTEXPR operator const Char *() const FMT_NOEXCEPT
{
return buffer;
}
FMT_CONSTEXPR const Char* begin() const FMT_NOEXCEPT { return buffer; } FMT_CONSTEXPR const Char *begin() const FMT_NOEXCEPT
FMT_CONSTEXPR const Char* end() const FMT_NOEXCEPT { {
return buffer;
}
FMT_CONSTEXPR const Char *end() const FMT_NOEXCEPT
{
return buffer + std::strlen(buffer); return buffer + std::strlen(buffer);
} }
private: private:
Char buffer[7u + 3u * 4u + 1u]; Char buffer[7u + 3u * 4u + 1u];
static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out, static FMT_CONSTEXPR void to_esc(uint8_t c, Char *out, char delimiter) FMT_NOEXCEPT
char delimiter) FMT_NOEXCEPT { {
out[0] = static_cast<Char>('0' + c / 100); out[0] = static_cast<Char>('0' + c / 100);
out[1] = static_cast<Char>('0' + c / 10 % 10); out[1] = static_cast<Char>('0' + c / 10 % 10);
out[2] = static_cast<Char>('0' + c % 10); out[2] = static_cast<Char>('0' + c % 10);
@ -427,80 +483,90 @@ template <typename Char> struct ansi_color_escape {
} }
}; };
template <typename Char> template<typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color( FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color(internal::color_type foreground) FMT_NOEXCEPT
internal::color_type foreground) FMT_NOEXCEPT { {
return ansi_color_escape<Char>(foreground, internal::data::foreground_color); return ansi_color_escape<Char>(foreground, internal::data::foreground_color);
} }
template <typename Char> template<typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> make_background_color( FMT_CONSTEXPR ansi_color_escape<Char> make_background_color(internal::color_type background) FMT_NOEXCEPT
internal::color_type background) FMT_NOEXCEPT { {
return ansi_color_escape<Char>(background, internal::data::background_color); return ansi_color_escape<Char>(background, internal::data::background_color);
} }
template <typename Char> template<typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> make_emphasis(emphasis em) FMT_NOEXCEPT { FMT_CONSTEXPR ansi_color_escape<Char> make_emphasis(emphasis em) FMT_NOEXCEPT
{
return ansi_color_escape<Char>(em); return ansi_color_escape<Char>(em);
} }
template <typename Char> template<typename Char>
inline void fputs(const Char* chars, FILE* stream) FMT_NOEXCEPT { inline void fputs(const Char *chars, FILE *stream) FMT_NOEXCEPT
{
std::fputs(chars, stream); std::fputs(chars, stream);
} }
template <> template<>
inline void fputs<wchar_t>(const wchar_t* chars, FILE* stream) FMT_NOEXCEPT { inline void fputs<wchar_t>(const wchar_t *chars, FILE *stream) FMT_NOEXCEPT
{
std::fputws(chars, stream); std::fputws(chars, stream);
} }
template <typename Char> inline void reset_color(FILE* stream) FMT_NOEXCEPT { template<typename Char>
inline void reset_color(FILE *stream) FMT_NOEXCEPT
{
fputs(internal::data::reset_color, stream); fputs(internal::data::reset_color, stream);
} }
template <> inline void reset_color<wchar_t>(FILE* stream) FMT_NOEXCEPT { template<>
inline void reset_color<wchar_t>(FILE *stream) FMT_NOEXCEPT
{
fputs(internal::data::wreset_color, stream); fputs(internal::data::wreset_color, stream);
} }
template <typename Char> template<typename Char>
inline void reset_color(basic_memory_buffer<Char>& buffer) FMT_NOEXCEPT { inline void reset_color(basic_memory_buffer<Char> &buffer) FMT_NOEXCEPT
const char* begin = data::reset_color; {
const char* end = begin + sizeof(data::reset_color) - 1; const char *begin = data::reset_color;
const char *end = begin + sizeof(data::reset_color) - 1;
buffer.append(begin, end); buffer.append(begin, end);
} }
template <typename Char> template<typename Char>
void vformat_to(basic_memory_buffer<Char>& buf, const text_style& ts, void vformat_to(
basic_string_view<Char> format_str, basic_memory_buffer<Char> &buf, const text_style &ts, basic_string_view<Char> format_str, basic_format_args<buffer_context<Char>> args)
basic_format_args<buffer_context<Char>> args) { {
bool has_style = false; bool has_style = false;
if (ts.has_emphasis()) { if (ts.has_emphasis())
{
has_style = true; has_style = true;
auto emphasis = internal::make_emphasis<Char>(ts.get_emphasis()); auto emphasis = internal::make_emphasis<Char>(ts.get_emphasis());
buf.append(emphasis.begin(), emphasis.end()); buf.append(emphasis.begin(), emphasis.end());
} }
if (ts.has_foreground()) { if (ts.has_foreground())
{
has_style = true; has_style = true;
auto foreground = auto foreground = internal::make_foreground_color<Char>(ts.get_foreground());
internal::make_foreground_color<Char>(ts.get_foreground());
buf.append(foreground.begin(), foreground.end()); buf.append(foreground.begin(), foreground.end());
} }
if (ts.has_background()) { if (ts.has_background())
{
has_style = true; has_style = true;
auto background = auto background = internal::make_background_color<Char>(ts.get_background());
internal::make_background_color<Char>(ts.get_background());
buf.append(background.begin(), background.end()); buf.append(background.begin(), background.end());
} }
vformat_to(buf, format_str, args); vformat_to(buf, format_str, args);
if (has_style) { if (has_style)
{
internal::reset_color<Char>(buf); internal::reset_color<Char>(buf);
} }
} }
} // namespace internal } // namespace internal
template <typename S, typename Char = char_t<S>> template<typename S, typename Char = char_t<S>>
void vprint(std::FILE* f, const text_style& ts, const S& format, void vprint(std::FILE *f, const text_style &ts, const S &format, basic_format_args<buffer_context<Char>> args)
basic_format_args<buffer_context<Char>> args) { {
basic_memory_buffer<Char> buf; basic_memory_buffer<Char> buf;
internal::vformat_to(buf, ts, to_string_view(format), args); internal::vformat_to(buf, ts, to_string_view(format), args);
buf.push_back(Char(0)); buf.push_back(Char(0));
@ -514,10 +580,9 @@ void vprint(std::FILE* f, const text_style& ts, const S& format,
fmt::print(fmt::emphasis::bold | fg(fmt::color::red), fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
"Elapsed time: {0:.2f} seconds", 1.23); "Elapsed time: {0:.2f} seconds", 1.23);
*/ */
template <typename S, typename... Args, template<typename S, typename... Args, FMT_ENABLE_IF(internal::is_string<S>::value)>
FMT_ENABLE_IF(internal::is_string<S>::value)> void print(std::FILE *f, const text_style &ts, const S &format_str, const Args &... args)
void print(std::FILE* f, const text_style& ts, const S& format_str, {
const Args&... args) {
internal::check_format_string<Args...>(format_str); internal::check_format_string<Args...>(format_str);
using context = buffer_context<char_t<S>>; using context = buffer_context<char_t<S>>;
format_arg_store<context, Args...> as{args...}; format_arg_store<context, Args...> as{args...};
@ -531,16 +596,15 @@ void print(std::FILE* f, const text_style& ts, const S& format_str,
fmt::print(fmt::emphasis::bold | fg(fmt::color::red), fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
"Elapsed time: {0:.2f} seconds", 1.23); "Elapsed time: {0:.2f} seconds", 1.23);
*/ */
template <typename S, typename... Args, template<typename S, typename... Args, FMT_ENABLE_IF(internal::is_string<S>::value)>
FMT_ENABLE_IF(internal::is_string<S>::value)> void print(const text_style &ts, const S &format_str, const Args &... args)
void print(const text_style& ts, const S& format_str, const Args&... args) { {
return print(stdout, ts, format_str, args...); return print(stdout, ts, format_str, args...);
} }
template <typename S, typename Char = char_t<S>> template<typename S, typename Char = char_t<S>>
inline std::basic_string<Char> vformat( inline std::basic_string<Char> vformat(const text_style &ts, const S &format_str, basic_format_args<buffer_context<Char>> args)
const text_style& ts, const S& format_str, {
basic_format_args<buffer_context<Char>> args) {
basic_memory_buffer<Char> buf; basic_memory_buffer<Char> buf;
internal::vformat_to(buf, ts, to_string_view(format_str), args); internal::vformat_to(buf, ts, to_string_view(format_str), args);
return fmt::to_string(buf); return fmt::to_string(buf);
@ -558,11 +622,10 @@ inline std::basic_string<Char> vformat(
"The answer is {}", 42); "The answer is {}", 42);
\endrst \endrst
*/ */
template <typename S, typename... Args, typename Char = char_t<S>> template<typename S, typename... Args, typename Char = char_t<S>>
inline std::basic_string<Char> format(const text_style& ts, const S& format_str, inline std::basic_string<Char> format(const text_style &ts, const S &format_str, const Args &... args)
const Args&... args) { {
return vformat(ts, to_string_view(format_str), return vformat(ts, 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

View File

@ -16,86 +16,128 @@ namespace internal {
// Part of a compiled format string. It can be either literal text or a // Part of a compiled format string. It can be either literal text or a
// replacement field. // replacement field.
template <typename Char> struct format_part { template<typename Char>
enum class kind { arg_index, arg_name, text, replacement }; struct format_part
{
enum class kind
{
arg_index,
arg_name,
text,
replacement
};
struct replacement { struct replacement
{
arg_ref<Char> arg_id; arg_ref<Char> arg_id;
dynamic_format_specs<Char> specs; dynamic_format_specs<Char> specs;
}; };
kind part_kind; kind part_kind;
union value { union value
{
int arg_index; int arg_index;
basic_string_view<Char> str; basic_string_view<Char> str;
replacement repl; replacement repl;
FMT_CONSTEXPR value(int index = 0) : arg_index(index) {} FMT_CONSTEXPR value(int index = 0)
FMT_CONSTEXPR value(basic_string_view<Char> s) : str(s) {} : arg_index(index)
FMT_CONSTEXPR value(replacement r) : repl(r) {} {}
FMT_CONSTEXPR value(basic_string_view<Char> s)
: str(s)
{}
FMT_CONSTEXPR value(replacement r)
: repl(r)
{}
} val; } val;
// Position past the end of the argument id. // Position past the end of the argument id.
const Char* arg_id_end = nullptr; const Char *arg_id_end = nullptr;
FMT_CONSTEXPR format_part(kind k = kind::arg_index, value v = {}) FMT_CONSTEXPR format_part(kind k = kind::arg_index, value v = {})
: part_kind(k), val(v) {} : part_kind(k)
, val(v)
{}
static FMT_CONSTEXPR format_part make_arg_index(int index) { static FMT_CONSTEXPR format_part make_arg_index(int index)
{
return format_part(kind::arg_index, index); return format_part(kind::arg_index, index);
} }
static FMT_CONSTEXPR format_part make_arg_name(basic_string_view<Char> name) { static FMT_CONSTEXPR format_part make_arg_name(basic_string_view<Char> name)
{
return format_part(kind::arg_name, name); return format_part(kind::arg_name, name);
} }
static FMT_CONSTEXPR format_part make_text(basic_string_view<Char> text) { static FMT_CONSTEXPR format_part make_text(basic_string_view<Char> text)
{
return format_part(kind::text, text); return format_part(kind::text, text);
} }
static FMT_CONSTEXPR format_part make_replacement(replacement repl) { static FMT_CONSTEXPR format_part make_replacement(replacement repl)
{
return format_part(kind::replacement, repl); return format_part(kind::replacement, repl);
} }
}; };
template <typename Char> struct part_counter { template<typename Char>
struct part_counter
{
unsigned num_parts = 0; unsigned num_parts = 0;
FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) { FMT_CONSTEXPR void on_text(const Char *begin, const Char *end)
if (begin != end) ++num_parts; {
if (begin != end)
++num_parts;
} }
FMT_CONSTEXPR void on_arg_id() { ++num_parts; } FMT_CONSTEXPR void on_arg_id()
FMT_CONSTEXPR void on_arg_id(int) { ++num_parts; } {
FMT_CONSTEXPR void on_arg_id(basic_string_view<Char>) { ++num_parts; } ++num_parts;
}
FMT_CONSTEXPR void on_arg_id(int)
{
++num_parts;
}
FMT_CONSTEXPR void on_arg_id(basic_string_view<Char>)
{
++num_parts;
}
FMT_CONSTEXPR void on_replacement_field(const Char*) {} FMT_CONSTEXPR void on_replacement_field(const Char *) {}
FMT_CONSTEXPR const Char* on_format_specs(const Char* begin, FMT_CONSTEXPR const Char *on_format_specs(const Char *begin, const Char *end)
const Char* end) { {
// Find the matching brace. // Find the matching brace.
unsigned brace_counter = 0; unsigned brace_counter = 0;
for (; begin != end; ++begin) { for (; begin != end; ++begin)
if (*begin == '{') { {
if (*begin == '{')
{
++brace_counter; ++brace_counter;
} else if (*begin == '}') { }
if (brace_counter == 0u) break; else if (*begin == '}')
{
if (brace_counter == 0u)
break;
--brace_counter; --brace_counter;
} }
} }
return begin; return begin;
} }
FMT_CONSTEXPR void on_error(const char*) {} FMT_CONSTEXPR void on_error(const char *) {}
}; };
// Counts the number of parts in a format string. // Counts the number of parts in a format string.
template <typename Char> template<typename Char>
FMT_CONSTEXPR unsigned count_parts(basic_string_view<Char> format_str) { FMT_CONSTEXPR unsigned count_parts(basic_string_view<Char> format_str)
{
part_counter<Char> counter; part_counter<Char> counter;
parse_format_string<true>(format_str, counter); parse_format_string<true>(format_str, counter);
return counter.num_parts; return counter.num_parts;
} }
template <typename Char, typename PartHandler> template<typename Char, typename PartHandler>
class format_string_compiler : public error_handler { class format_string_compiler : public error_handler
private: {
private:
using part = format_part<Char>; using part = format_part<Char>;
PartHandler handler_; PartHandler handler_;
@ -103,46 +145,49 @@ class format_string_compiler : public error_handler {
basic_string_view<Char> format_str_; basic_string_view<Char> format_str_;
basic_format_parse_context<Char> parse_context_; basic_format_parse_context<Char> parse_context_;
public: public:
FMT_CONSTEXPR format_string_compiler(basic_string_view<Char> format_str, FMT_CONSTEXPR format_string_compiler(basic_string_view<Char> format_str, PartHandler handler)
PartHandler handler) : handler_(handler)
: handler_(handler), , format_str_(format_str)
format_str_(format_str), , parse_context_(format_str)
parse_context_(format_str) {} {}
FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) { FMT_CONSTEXPR void on_text(const Char *begin, const Char *end)
{
if (begin != end) if (begin != end)
handler_(part::make_text({begin, to_unsigned(end - begin)})); handler_(part::make_text({begin, to_unsigned(end - begin)}));
} }
FMT_CONSTEXPR void on_arg_id() { FMT_CONSTEXPR void on_arg_id()
{
part_ = part::make_arg_index(parse_context_.next_arg_id()); part_ = part::make_arg_index(parse_context_.next_arg_id());
} }
FMT_CONSTEXPR void on_arg_id(int id) { FMT_CONSTEXPR void on_arg_id(int id)
{
parse_context_.check_arg_id(id); parse_context_.check_arg_id(id);
part_ = part::make_arg_index(id); part_ = part::make_arg_index(id);
} }
FMT_CONSTEXPR void on_arg_id(basic_string_view<Char> id) { FMT_CONSTEXPR void on_arg_id(basic_string_view<Char> id)
{
part_ = part::make_arg_name(id); part_ = part::make_arg_name(id);
} }
FMT_CONSTEXPR void on_replacement_field(const Char* ptr) { FMT_CONSTEXPR void on_replacement_field(const Char *ptr)
{
part_.arg_id_end = ptr; part_.arg_id_end = ptr;
handler_(part_); handler_(part_);
} }
FMT_CONSTEXPR const Char* on_format_specs(const Char* begin, FMT_CONSTEXPR const Char *on_format_specs(const Char *begin, const Char *end)
const Char* end) { {
auto repl = typename part::replacement(); auto repl = typename part::replacement();
dynamic_specs_handler<basic_format_parse_context<Char>> handler( dynamic_specs_handler<basic_format_parse_context<Char>> handler(repl.specs, parse_context_);
repl.specs, parse_context_);
auto it = parse_format_specs(begin, end, handler); auto it = parse_format_specs(begin, end, handler);
if (*it != '}') on_error("missing '}' in format string"); if (*it != '}')
repl.arg_id = part_.part_kind == part::kind::arg_index on_error("missing '}' in format string");
? arg_ref<Char>(part_.val.arg_index) repl.arg_id = part_.part_kind == part::kind::arg_index ? arg_ref<Char>(part_.val.arg_index) : arg_ref<Char>(part_.val.str);
: arg_ref<Char>(part_.val.str);
auto part = part::make_replacement(repl); auto part = part::make_replacement(repl);
part.arg_id_end = begin; part.arg_id_end = begin;
handler_(part); handler_(part);
@ -151,44 +196,40 @@ class format_string_compiler : public error_handler {
}; };
// Compiles a format string and invokes handler(part) for each parsed part. // Compiles a format string and invokes handler(part) for each parsed part.
template <bool IS_CONSTEXPR, typename Char, typename PartHandler> template<bool IS_CONSTEXPR, typename Char, typename PartHandler>
FMT_CONSTEXPR void compile_format_string(basic_string_view<Char> format_str, FMT_CONSTEXPR void compile_format_string(basic_string_view<Char> format_str, PartHandler handler)
PartHandler handler) { {
parse_format_string<IS_CONSTEXPR>( parse_format_string<IS_CONSTEXPR>(format_str, format_string_compiler<Char, PartHandler>(format_str, handler));
format_str,
format_string_compiler<Char, PartHandler>(format_str, handler));
} }
template <typename Range, typename Context, typename Id> template<typename Range, typename Context, typename Id>
void format_arg( void format_arg(basic_format_parse_context<typename Range::value_type> &parse_ctx, Context &ctx, Id arg_id)
basic_format_parse_context<typename Range::value_type>& parse_ctx, {
Context& ctx, Id arg_id) { ctx.advance_to(visit_format_arg(arg_formatter<Range>(ctx, &parse_ctx), ctx.arg(arg_id)));
ctx.advance_to(
visit_format_arg(arg_formatter<Range>(ctx, &parse_ctx), ctx.arg(arg_id)));
} }
// vformat_to is defined in a subnamespace to prevent ADL. // vformat_to is defined in a subnamespace to prevent ADL.
namespace cf { namespace cf {
template <typename Context, typename Range, typename CompiledFormat> template<typename Context, typename Range, typename CompiledFormat>
auto vformat_to(Range out, CompiledFormat& cf, basic_format_args<Context> args) auto vformat_to(Range out, CompiledFormat &cf, basic_format_args<Context> args) -> typename Context::iterator
-> typename Context::iterator { {
using char_type = typename Context::char_type; using char_type = typename Context::char_type;
basic_format_parse_context<char_type> parse_ctx( basic_format_parse_context<char_type> parse_ctx(to_string_view(cf.format_str_));
to_string_view(cf.format_str_));
Context ctx(out.begin(), args); Context ctx(out.begin(), args);
const auto& parts = cf.parts(); const auto &parts = cf.parts();
for (auto part_it = std::begin(parts); part_it != std::end(parts); for (auto part_it = std::begin(parts); part_it != std::end(parts); ++part_it)
++part_it) { {
const auto& part = *part_it; const auto &part = *part_it;
const auto& value = part.val; const auto &value = part.val;
using format_part_t = format_part<char_type>; using format_part_t = format_part<char_type>;
switch (part.part_kind) { switch (part.part_kind)
{
case format_part_t::kind::text: { case format_part_t::kind::text: {
const auto text = value.str; const auto text = value.str;
auto output = ctx.out(); auto output = ctx.out();
auto&& it = reserve(output, text.size()); auto &&it = reserve(output, text.size());
it = std::copy_n(text.begin(), text.size(), it); it = std::copy_n(text.begin(), text.size(), it);
ctx.advance_to(output); ctx.advance_to(output);
break; break;
@ -205,27 +246,27 @@ auto vformat_to(Range out, CompiledFormat& cf, basic_format_args<Context> args)
break; break;
case format_part_t::kind::replacement: { case format_part_t::kind::replacement: {
const auto& arg_id_value = value.repl.arg_id.val; const auto &arg_id_value = value.repl.arg_id.val;
const auto arg = value.repl.arg_id.kind == arg_id_kind::index const auto arg = value.repl.arg_id.kind == arg_id_kind::index ? ctx.arg(arg_id_value.index) : ctx.arg(arg_id_value.name);
? ctx.arg(arg_id_value.index)
: ctx.arg(arg_id_value.name);
auto specs = value.repl.specs; auto specs = value.repl.specs;
handle_dynamic_spec<width_checker>(specs.width, specs.width_ref, ctx); handle_dynamic_spec<width_checker>(specs.width, specs.width_ref, ctx);
handle_dynamic_spec<precision_checker>(specs.precision, handle_dynamic_spec<precision_checker>(specs.precision, specs.precision_ref, ctx);
specs.precision_ref, ctx);
error_handler h; error_handler h;
numeric_specs_checker<error_handler> checker(h, arg.type()); numeric_specs_checker<error_handler> checker(h, arg.type());
if (specs.align == align::numeric) checker.require_numeric_argument(); if (specs.align == align::numeric)
if (specs.sign != sign::none) checker.check_sign(); checker.require_numeric_argument();
if (specs.alt) checker.require_numeric_argument(); if (specs.sign != sign::none)
if (specs.precision >= 0) checker.check_precision(); checker.check_sign();
if (specs.alt)
checker.require_numeric_argument();
if (specs.precision >= 0)
checker.check_precision();
advance_to(parse_ctx, part.arg_id_end); advance_to(parse_ctx, part.arg_id_end);
ctx.advance_to( ctx.advance_to(visit_format_arg(arg_formatter<Range>(ctx, nullptr, &specs), arg));
visit_format_arg(arg_formatter<Range>(ctx, nullptr, &specs), arg));
break; break;
} }
} }
@ -234,58 +275,67 @@ auto vformat_to(Range out, CompiledFormat& cf, basic_format_args<Context> args)
} }
} // namespace cf } // namespace cf
struct basic_compiled_format {}; struct basic_compiled_format
{};
template <typename S, typename = void> template<typename S, typename = void>
struct compiled_format_base : basic_compiled_format { struct compiled_format_base : basic_compiled_format
{
using char_type = char_t<S>; using char_type = char_t<S>;
using parts_container = std::vector<internal::format_part<char_type>>; using parts_container = std::vector<internal::format_part<char_type>>;
parts_container compiled_parts; parts_container compiled_parts;
explicit compiled_format_base(basic_string_view<char_type> format_str) { explicit compiled_format_base(basic_string_view<char_type> format_str)
compile_format_string<false>(format_str, {
[this](const format_part<char_type>& part) { compile_format_string<false>(format_str, [this](const format_part<char_type> &part) { compiled_parts.push_back(part); });
compiled_parts.push_back(part);
});
} }
const parts_container& parts() const { return compiled_parts; } const parts_container &parts() const
{
return compiled_parts;
}
}; };
template <typename Char, unsigned N> struct format_part_array { template<typename Char, unsigned N>
struct format_part_array
{
format_part<Char> data[N] = {}; format_part<Char> data[N] = {};
FMT_CONSTEXPR format_part_array() = default; FMT_CONSTEXPR format_part_array() = default;
}; };
template <typename Char, unsigned N> template<typename Char, unsigned N>
FMT_CONSTEXPR format_part_array<Char, N> compile_to_parts( FMT_CONSTEXPR format_part_array<Char, N> compile_to_parts(basic_string_view<Char> format_str)
basic_string_view<Char> format_str) { {
format_part_array<Char, N> parts; format_part_array<Char, N> parts;
unsigned counter = 0; unsigned counter = 0;
// This is not a lambda for compatibility with older compilers. // This is not a lambda for compatibility with older compilers.
struct { struct
format_part<Char>* parts; {
unsigned* counter; format_part<Char> *parts;
FMT_CONSTEXPR void operator()(const format_part<Char>& part) { unsigned *counter;
FMT_CONSTEXPR void operator()(const format_part<Char> &part)
{
parts[(*counter)++] = part; parts[(*counter)++] = part;
} }
} collector{parts.data, &counter}; } collector{parts.data, &counter};
compile_format_string<true>(format_str, collector); compile_format_string<true>(format_str, collector);
if (counter < N) { if (counter < N)
parts.data[counter] = {
format_part<Char>::make_text(basic_string_view<Char>()); parts.data[counter] = format_part<Char>::make_text(basic_string_view<Char>());
} }
return parts; return parts;
} }
template <typename T> constexpr const T& constexpr_max(const T& a, const T& b) { template<typename T>
constexpr const T &constexpr_max(const T &a, const T &b)
{
return (a < b) ? b : a; return (a < b) ? b : a;
} }
template <typename S> template<typename S>
struct compiled_format_base<S, enable_if_t<is_compile_string<S>::value>> struct compiled_format_base<S, enable_if_t<is_compile_string<S>::value>> : basic_compiled_format
: basic_compiled_format { {
using char_type = char_t<S>; using char_type = char_t<S>;
FMT_CONSTEXPR explicit compiled_format_base(basic_string_view<char_type>) {} FMT_CONSTEXPR explicit compiled_format_base(basic_string_view<char_type>) {}
@ -293,47 +343,49 @@ struct compiled_format_base<S, enable_if_t<is_compile_string<S>::value>>
// Workaround for old compilers. Format string compilation will not be // Workaround for old compilers. Format string compilation will not be
// performed there anyway. // performed there anyway.
#if FMT_USE_CONSTEXPR #if FMT_USE_CONSTEXPR
static FMT_CONSTEXPR_DECL const unsigned num_format_parts = static FMT_CONSTEXPR_DECL const unsigned num_format_parts = constexpr_max(count_parts(to_string_view(S())), 1u);
constexpr_max(count_parts(to_string_view(S())), 1u);
#else #else
static const unsigned num_format_parts = 1; static const unsigned num_format_parts = 1;
#endif #endif
using parts_container = format_part<char_type>[num_format_parts]; using parts_container = format_part<char_type>[num_format_parts];
const parts_container& parts() const { const parts_container &parts() const
static FMT_CONSTEXPR_DECL const auto compiled_parts = {
compile_to_parts<char_type, num_format_parts>( static FMT_CONSTEXPR_DECL const auto compiled_parts = compile_to_parts<char_type, num_format_parts>(internal::to_string_view(S()));
internal::to_string_view(S()));
return compiled_parts.data; return compiled_parts.data;
} }
}; };
template <typename S, typename... Args> template<typename S, typename... Args>
class compiled_format : private compiled_format_base<S> { class compiled_format : private compiled_format_base<S>
public: {
public:
using typename compiled_format_base<S>::char_type; using typename compiled_format_base<S>::char_type;
private: private:
basic_string_view<char_type> format_str_; basic_string_view<char_type> format_str_;
template <typename Context, typename Range, typename CompiledFormat> template<typename Context, typename Range, typename CompiledFormat>
friend auto cf::vformat_to(Range out, CompiledFormat& cf, friend auto cf::vformat_to(Range out, CompiledFormat &cf, basic_format_args<Context> args) -> typename Context::iterator;
basic_format_args<Context> args) ->
typename Context::iterator;
public: public:
compiled_format() = delete; compiled_format() = delete;
explicit constexpr compiled_format(basic_string_view<char_type> format_str) explicit constexpr compiled_format(basic_string_view<char_type> format_str)
: compiled_format_base<S>(format_str), format_str_(format_str) {} : compiled_format_base<S>(format_str)
, format_str_(format_str)
{}
}; };
#ifdef __cpp_if_constexpr #ifdef __cpp_if_constexpr
template <typename... Args> struct type_list {}; template<typename... Args>
struct type_list
{};
// Returns a reference to the argument at index N from [first, rest...]. // Returns a reference to the argument at index N from [first, rest...].
template <int N, typename T, typename... Args> template<int N, typename T, typename... Args>
constexpr const auto& get(const T& first, const Args&... rest) { constexpr const auto &get(const T &first, const Args &... rest)
{
static_assert(N < 1 + sizeof...(Args), "index is out of bounds"); static_assert(N < 1 + sizeof...(Args), "index is out of bounds");
if constexpr (N == 0) if constexpr (N == 0)
return first; return first;
@ -341,242 +393,271 @@ constexpr const auto& get(const T& first, const Args&... rest) {
return get<N - 1>(rest...); return get<N - 1>(rest...);
} }
template <int N, typename> struct get_type_impl; template<int N, typename>
struct get_type_impl;
template <int N, typename... Args> struct get_type_impl<N, type_list<Args...>> { template<int N, typename... Args>
struct get_type_impl<N, type_list<Args...>>
{
using type = remove_cvref_t<decltype(get<N>(std::declval<Args>()...))>; using type = remove_cvref_t<decltype(get<N>(std::declval<Args>()...))>;
}; };
template <int N, typename T> template<int N, typename T>
using get_type = typename get_type_impl<N, T>::type; using get_type = typename get_type_impl<N, T>::type;
template <typename Char> struct text { template<typename Char>
struct text
{
basic_string_view<Char> data; basic_string_view<Char> data;
using char_type = Char; using char_type = Char;
template <typename OutputIt, typename... Args> template<typename OutputIt, typename... Args>
OutputIt format(OutputIt out, const Args&...) const { OutputIt format(OutputIt out, const Args &...) const
{
// TODO: reserve // TODO: reserve
return copy_str<Char>(data.begin(), data.end(), out); return copy_str<Char>(data.begin(), data.end(), out);
} }
}; };
template <typename Char> template<typename Char>
constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos, constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos, size_t size)
size_t size) { {
return {{&s[pos], size}}; return {{&s[pos], size}};
} }
template <typename Char, typename OutputIt, typename T, template<typename Char, typename OutputIt, typename T, std::enable_if_t<std::is_integral_v<T>, int> = 0>
std::enable_if_t<std::is_integral_v<T>, int> = 0> OutputIt format_default(OutputIt out, T value)
OutputIt format_default(OutputIt out, T value) { {
// TODO: reserve // TODO: reserve
format_int fi(value); format_int fi(value);
return std::copy(fi.data(), fi.data() + fi.size(), out); return std::copy(fi.data(), fi.data() + fi.size(), out);
} }
template <typename Char, typename OutputIt> template<typename Char, typename OutputIt>
OutputIt format_default(OutputIt out, double value) { OutputIt format_default(OutputIt out, double value)
{
writer w(out); writer w(out);
w.write(value); w.write(value);
return w.out(); return w.out();
} }
template <typename Char, typename OutputIt> template<typename Char, typename OutputIt>
OutputIt format_default(OutputIt out, Char value) { OutputIt format_default(OutputIt out, Char value)
{
*out++ = value; *out++ = value;
return out; return out;
} }
template <typename Char, typename OutputIt> template<typename Char, typename OutputIt>
OutputIt format_default(OutputIt out, const Char* value) { OutputIt format_default(OutputIt out, const Char *value)
{
auto length = std::char_traits<Char>::length(value); auto length = std::char_traits<Char>::length(value);
return copy_str<Char>(value, value + length, out); return copy_str<Char>(value, value + length, out);
} }
// A replacement field that refers to argument N. // A replacement field that refers to argument N.
template <typename Char, typename T, int N> struct field { template<typename Char, typename T, int N>
struct field
{
using char_type = Char; using char_type = Char;
template <typename OutputIt, typename... Args> template<typename OutputIt, typename... Args>
OutputIt format(OutputIt out, const Args&... args) const { OutputIt format(OutputIt out, const Args &... args) const
{
// This ensures that the argument type is convertile to `const T&`. // This ensures that the argument type is convertile to `const T&`.
const T& arg = get<N>(args...); const T &arg = get<N>(args...);
return format_default<Char>(out, arg); return format_default<Char>(out, arg);
} }
}; };
template <typename L, typename R> struct concat { template<typename L, typename R>
struct concat
{
L lhs; L lhs;
R rhs; R rhs;
using char_type = typename L::char_type; using char_type = typename L::char_type;
template <typename OutputIt, typename... Args> template<typename OutputIt, typename... Args>
OutputIt format(OutputIt out, const Args&... args) const { OutputIt format(OutputIt out, const Args &... args) const
{
out = lhs.format(out, args...); out = lhs.format(out, args...);
return rhs.format(out, args...); return rhs.format(out, args...);
} }
}; };
template <typename L, typename R> template<typename L, typename R>
constexpr concat<L, R> make_concat(L lhs, R rhs) { constexpr concat<L, R> make_concat(L lhs, R rhs)
{
return {lhs, rhs}; return {lhs, rhs};
} }
struct unknown_format {}; struct unknown_format
{};
template <typename Char> template<typename Char>
constexpr size_t parse_text(basic_string_view<Char> str, size_t pos) { constexpr size_t parse_text(basic_string_view<Char> str, size_t pos)
for (size_t size = str.size(); pos != size; ++pos) { {
if (str[pos] == '{' || str[pos] == '}') break; for (size_t size = str.size(); pos != size; ++pos)
{
if (str[pos] == '{' || str[pos] == '}')
break;
} }
return pos; return pos;
} }
template <typename Args, size_t POS, int ID, typename S> template<typename Args, size_t POS, int ID, typename S>
constexpr auto compile_format_string(S format_str); constexpr auto compile_format_string(S format_str);
template <typename Args, size_t POS, int ID, typename T, typename S> template<typename Args, size_t POS, int ID, typename T, typename S>
constexpr auto parse_tail(T head, S format_str) { constexpr auto parse_tail(T head, S format_str)
if constexpr (POS != to_string_view(format_str).size()) { {
if constexpr (POS != to_string_view(format_str).size())
{
constexpr auto tail = compile_format_string<Args, POS, ID>(format_str); constexpr auto tail = compile_format_string<Args, POS, ID>(format_str);
if constexpr (std::is_same<remove_cvref_t<decltype(tail)>, if constexpr (std::is_same<remove_cvref_t<decltype(tail)>, unknown_format>())
unknown_format>())
return tail; return tail;
else else
return make_concat(head, tail); return make_concat(head, tail);
} else { }
else
{
return head; return head;
} }
} }
// Compiles a non-empty format string and returns the compiled representation // Compiles a non-empty format string and returns the compiled representation
// or unknown_format() on unrecognized input. // or unknown_format() on unrecognized input.
template <typename Args, size_t POS, int ID, typename S> template<typename Args, size_t POS, int ID, typename S>
constexpr auto compile_format_string(S format_str) { constexpr auto compile_format_string(S format_str)
{
using char_type = typename S::char_type; using char_type = typename S::char_type;
constexpr basic_string_view<char_type> str = format_str; constexpr basic_string_view<char_type> str = format_str;
if constexpr (str[POS] == '{') { if constexpr (str[POS] == '{')
{
if (POS + 1 == str.size()) if (POS + 1 == str.size())
throw format_error("unmatched '{' in format string"); throw format_error("unmatched '{' in format string");
if constexpr (str[POS + 1] == '{') { if constexpr (str[POS + 1] == '{')
{
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str); return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
} else if constexpr (str[POS + 1] == '}') { }
else if constexpr (str[POS + 1] == '}')
{
using type = get_type<ID, Args>; using type = get_type<ID, Args>;
if constexpr (std::is_same<type, int>::value) { if constexpr (std::is_same<type, int>::value)
return parse_tail<Args, POS + 2, ID + 1>(field<char_type, type, ID>(), {
format_str); return parse_tail<Args, POS + 2, ID + 1>(field<char_type, type, ID>(), format_str);
} else { }
else
{
return unknown_format(); return unknown_format();
} }
} else { }
else
{
return unknown_format(); return unknown_format();
} }
} else if constexpr (str[POS] == '}') { }
else if constexpr (str[POS] == '}')
{
if (POS + 1 == str.size()) if (POS + 1 == str.size())
throw format_error("unmatched '}' in format string"); throw format_error("unmatched '}' in format string");
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str); return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
} else { }
else
{
constexpr auto end = parse_text(str, POS + 1); constexpr auto end = parse_text(str, POS + 1);
return parse_tail<Args, end, ID>(make_text(str, POS, end - POS), return parse_tail<Args, end, ID>(make_text(str, POS, end - POS), format_str);
format_str);
} }
} }
#endif // __cpp_if_constexpr #endif // __cpp_if_constexpr
} // namespace internal } // namespace internal
#if FMT_USE_CONSTEXPR #if FMT_USE_CONSTEXPR
# ifdef __cpp_if_constexpr #ifdef __cpp_if_constexpr
template <typename... Args, typename S, template<typename... Args, typename S, FMT_ENABLE_IF(is_compile_string<S>::value)>
FMT_ENABLE_IF(is_compile_string<S>::value)> constexpr auto compile(S format_str)
constexpr auto compile(S format_str) { {
constexpr basic_string_view<typename S::char_type> str = format_str; constexpr basic_string_view<typename S::char_type> str = format_str;
if constexpr (str.size() == 0) { if constexpr (str.size() == 0)
{
return internal::make_text(str, 0, 0); return internal::make_text(str, 0, 0);
} else { }
constexpr auto result = else
internal::compile_format_string<internal::type_list<Args...>, 0, 0>( {
format_str); constexpr auto result = internal::compile_format_string<internal::type_list<Args...>, 0, 0>(format_str);
if constexpr (std::is_same<remove_cvref_t<decltype(result)>, if constexpr (std::is_same<remove_cvref_t<decltype(result)>, internal::unknown_format>())
internal::unknown_format>()) { {
return internal::compiled_format<S, Args...>(to_string_view(format_str)); return internal::compiled_format<S, Args...>(to_string_view(format_str));
} else { }
else
{
return result; return result;
} }
} }
} }
template <typename CompiledFormat, typename... Args, template<typename CompiledFormat, typename... Args, typename Char = typename CompiledFormat::char_type,
typename Char = typename CompiledFormat::char_type, FMT_ENABLE_IF(!std::is_base_of<internal::basic_compiled_format, CompiledFormat>::value)>
FMT_ENABLE_IF(!std::is_base_of<internal::basic_compiled_format, std::basic_string<Char> format(const CompiledFormat &cf, const Args &... args)
CompiledFormat>::value)> {
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
basic_memory_buffer<Char> buffer; basic_memory_buffer<Char> buffer;
cf.format(std::back_inserter(buffer), args...); cf.format(std::back_inserter(buffer), args...);
return to_string(buffer); return to_string(buffer);
} }
template <typename OutputIt, typename CompiledFormat, typename... Args, template<typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(!std::is_base_of<internal::basic_compiled_format, FMT_ENABLE_IF(!std::is_base_of<internal::basic_compiled_format, CompiledFormat>::value)>
CompiledFormat>::value)> OutputIt format_to(OutputIt out, const CompiledFormat &cf, const Args &... args)
OutputIt format_to(OutputIt out, const CompiledFormat& cf, {
const Args&... args) {
return cf.format(out, args...); return cf.format(out, args...);
} }
# else #else
template <typename... Args, typename S, template<typename... Args, typename S, FMT_ENABLE_IF(is_compile_string<S>::value)>
FMT_ENABLE_IF(is_compile_string<S>::value)> constexpr auto compile(S format_str) -> internal::compiled_format<S, Args...>
constexpr auto compile(S format_str) -> internal::compiled_format<S, Args...> { {
return internal::compiled_format<S, Args...>(to_string_view(format_str)); return internal::compiled_format<S, Args...>(to_string_view(format_str));
} }
# endif // __cpp_if_constexpr #endif // __cpp_if_constexpr
#endif // FMT_USE_CONSTEXPR #endif // FMT_USE_CONSTEXPR
// Compiles the format string which must be a string literal. // Compiles the format string which must be a string literal.
template <typename... Args, typename Char, size_t N> template<typename... Args, typename Char, size_t N>
auto compile(const Char (&format_str)[N]) auto compile(const Char (&format_str)[N]) -> internal::compiled_format<const Char *, Args...>
-> internal::compiled_format<const Char*, Args...> { {
return internal::compiled_format<const Char*, Args...>( return internal::compiled_format<const Char *, Args...>(basic_string_view<Char>(format_str, N - 1));
basic_string_view<Char>(format_str, N - 1));
} }
template <typename CompiledFormat, typename... Args, template<typename CompiledFormat, typename... Args, typename Char = typename CompiledFormat::char_type,
typename Char = typename CompiledFormat::char_type, FMT_ENABLE_IF(std::is_base_of<internal::basic_compiled_format, CompiledFormat>::value)>
FMT_ENABLE_IF(std::is_base_of<internal::basic_compiled_format, std::basic_string<Char> format(const CompiledFormat &cf, const Args &... args)
CompiledFormat>::value)> {
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
basic_memory_buffer<Char> buffer; basic_memory_buffer<Char> buffer;
using range = buffer_range<Char>; using range = buffer_range<Char>;
using context = buffer_context<Char>; using context = buffer_context<Char>;
internal::cf::vformat_to<context>(range(buffer), cf, internal::cf::vformat_to<context>(range(buffer), cf, {make_format_args<context>(args...)});
{make_format_args<context>(args...)});
return to_string(buffer); return to_string(buffer);
} }
template <typename OutputIt, typename CompiledFormat, typename... Args, template<typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(std::is_base_of<internal::basic_compiled_format, FMT_ENABLE_IF(std::is_base_of<internal::basic_compiled_format, CompiledFormat>::value)>
CompiledFormat>::value)> OutputIt format_to(OutputIt out, const CompiledFormat &cf, const Args &... args)
OutputIt format_to(OutputIt out, const CompiledFormat& cf, {
const Args&... args) {
using char_type = typename CompiledFormat::char_type; using char_type = typename CompiledFormat::char_type;
using range = internal::output_range<OutputIt, char_type>; using range = internal::output_range<OutputIt, char_type>;
using context = format_context_t<OutputIt, char_type>; using context = format_context_t<OutputIt, char_type>;
return internal::cf::vformat_to<context>( return internal::cf::vformat_to<context>(range(out), cf, {make_format_args<context>(args...)});
range(out), cf, {make_format_args<context>(args...)});
} }
template <typename OutputIt, typename CompiledFormat, typename... Args, template<typename OutputIt, typename CompiledFormat, typename... Args, FMT_ENABLE_IF(internal::is_output_iterator<OutputIt>::value)>
FMT_ENABLE_IF(internal::is_output_iterator<OutputIt>::value)> format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n, const CompiledFormat &cf, const Args &... args)
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n, {
const CompiledFormat& cf, auto it = format_to(internal::truncating_iterator<OutputIt>(out, n), cf, args...);
const Args&... args) {
auto it =
format_to(internal::truncating_iterator<OutputIt>(out, n), cf, args...);
return {it.base(), it.count()}; return {it.base(), it.count()};
} }
template <typename CompiledFormat, typename... Args> template<typename CompiledFormat, typename... Args>
std::size_t formatted_size(const CompiledFormat& cf, const Args&... args) { std::size_t formatted_size(const CompiledFormat &cf, const Args &... args)
{
return format_to(internal::counting_iterator(), cf, args...).count(); return format_to(internal::counting_iterator(), cf, args...).count();
} }

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,62 +14,51 @@
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, return vformat_to<arg_formatter<range>>(buf, to_string_view(format_str), args, internal::locale_ref(loc));
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,
format_args_t<OutputIt, Char> args) {
using range = internal::output_range<OutputIt, Char>; using range = internal::output_range<OutputIt, Char>;
return vformat_to<arg_formatter<range>>( return vformat_to<arg_formatter<range>>(range(out), to_string_view(format_str), args, internal::locale_ref(loc));
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), return vformat_to(out, loc, to_string_view(format_str), basic_format_args<context>(as));
basic_format_args<context>(as));
} }
FMT_END_NAMESPACE FMT_END_NAMESPACE

View File

@ -14,17 +14,21 @@
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>
{
private:
using int_type = typename std::basic_streambuf<Char>::int_type; using int_type = typename std::basic_streambuf<Char>::int_type;
using traits_type = typename std::basic_streambuf<Char>::traits_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
@ -32,55 +36,60 @@ template <class Char> class formatbuf : public std::basic_streambuf<Char> {
// 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())) if (!traits_type::eq_int_type(ch, traits_type::eof()))
buffer_.push_back(static_cast<Char>(ch)); buffer_.push_back(static_cast<Char>(ch));
return 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); buffer_.append(s, s + count);
return count; return count;
} }
}; };
template <typename Char> struct test_stream : std::basic_ostream<Char> { template<typename Char>
private: struct test_stream : std::basic_ostream<Char>
{
private:
// Hide all operator<< from std::basic_ostream<Char>. // Hide all operator<< from std::basic_ostream<Char>.
void_t<> operator<<(null<>); void_t<> operator<<(null<>);
void_t<> operator<<(const Char*); 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(); {
const Char *buf_data = buf.data();
using unsigned_streamsize = std::make_unsigned<std::streamsize>::type; using unsigned_streamsize = std::make_unsigned<std::streamsize>::type;
unsigned_streamsize size = buf.size(); unsigned_streamsize size = buf.size();
unsigned_streamsize max_size = to_unsigned(max_value<std::streamsize>()); unsigned_streamsize max_size = to_unsigned(max_value<std::streamsize>());
do { do
{
unsigned_streamsize n = size <= max_size ? size : max_size; unsigned_streamsize n = size <= max_size ? size : max_size;
os.write(buf_data, static_cast<std::streamsize>(n)); os.write(buf_data, static_cast<std::streamsize>(n));
buf_data += n; buf_data += n;
@ -88,23 +97,25 @@ void write(std::basic_ostream<Char>& os, buffer<Char>& buf) {
} while (size != 0); } 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.imbue(loc.get<std::locale>());
output.exceptions(std::ios_base::failbit | std::ios_base::badbit); output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
output << value; output << value;
buf.resize(buf.size()); 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; basic_memory_buffer<Char> buffer;
format_value(buffer, value, ctx.locale()); format_value(buffer, value, ctx.locale());
basic_string_view<Char> str(buffer.data(), buffer.size()); basic_string_view<Char> str(buffer.data(), buffer.size());
@ -113,9 +124,9 @@ struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
}; };
} // 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,11 +141,10 @@ 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

View File

@ -10,7 +10,7 @@
#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>
@ -21,54 +21,54 @@
#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,51 +100,67 @@ 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: {
private:
int value_; 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) {}
@ -152,12 +168,14 @@ class buffered_file {
// 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(); close();
file_ = other.file_; file_ = other.file_;
other.file_ = nullptr; other.file_ = nullptr;
@ -171,18 +189,23 @@ class buffered_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...));
} }
}; };
@ -194,16 +217,20 @@ 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: {
private:
int fd_; // File descriptor. 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. RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only. WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing. RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing.
@ -215,13 +242,17 @@ class file {
// 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(); close();
fd_ = other.fd_; fd_ = other.fd_;
other.fd_ = -1; other.fd_ = -1;
@ -232,7 +263,10 @@ class file {
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();
@ -242,10 +276,10 @@ class file {
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.
@ -257,15 +291,15 @@ class file {
// 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.
@ -274,42 +308,61 @@ long getpagesize();
#ifdef FMT_LOCALE #ifdef FMT_LOCALE
// A "C" numeric locale. // A "C" numeric locale.
class Locale { class Locale
private: {
# ifdef _WIN32 private:
#ifdef _WIN32
using locale_t = _locale_t; 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))
{
if (!locale_)
FMT_THROW(system_error(errno, "cannot create locale"));
}
~Locale()
{
freelocale(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; {
char *end = nullptr;
double result = strtod_l(str, &end, locale_); double result = strtod_l(str, &end, locale_);
str = end; str = end;
return result; return result;

View File

@ -18,207 +18,265 @@ namespace internal {
// Checks if a value fits in int - used to avoid warnings about comparing // Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers. // signed and unsigned integers.
template <bool IsSigned> struct int_checker { template<bool IsSigned>
template <typename T> static bool fits_in_int(T value) { struct int_checker
{
template<typename T>
static bool fits_in_int(T value)
{
unsigned max = max_value<int>(); unsigned max = max_value<int>();
return value <= max; return value <= max;
} }
static bool fits_in_int(bool) { return true; } static bool fits_in_int(bool)
}; {
return true;
template <> struct int_checker<true> {
template <typename T> static bool fits_in_int(T value) {
return value >= std::numeric_limits<int>::min() &&
value <= max_value<int>();
} }
static bool fits_in_int(int) { return true; }
}; };
class printf_precision_handler { template<>
public: struct int_checker<true>
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)> {
int operator()(T value) { template<typename T>
static bool fits_in_int(T value)
{
return value >= std::numeric_limits<int>::min() && value <= max_value<int>();
}
static bool fits_in_int(int)
{
return true;
}
};
class printf_precision_handler
{
public:
template<typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
int operator()(T value)
{
if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value)) if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
FMT_THROW(format_error("number is too big")); FMT_THROW(format_error("number is too big"));
return (std::max)(static_cast<int>(value), 0); return (std::max)(static_cast<int>(value), 0);
} }
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)> template<typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
int operator()(T) { int operator()(T)
{
FMT_THROW(format_error("precision is not integer")); FMT_THROW(format_error("precision is not integer"));
return 0; return 0;
} }
}; };
// An argument visitor that returns true iff arg is a zero integer. // An argument visitor that returns true iff arg is a zero integer.
class is_zero_int { class is_zero_int
public: {
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)> public:
bool operator()(T value) { template<typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
bool operator()(T value)
{
return value == 0; return value == 0;
} }
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)> template<typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
bool operator()(T) { bool operator()(T)
{
return false; return false;
} }
}; };
template <typename T> struct make_unsigned_or_bool : std::make_unsigned<T> {}; template<typename T>
struct make_unsigned_or_bool : std::make_unsigned<T>
{};
template <> struct make_unsigned_or_bool<bool> { using type = bool; }; template<>
struct make_unsigned_or_bool<bool>
{
using type = bool;
};
template <typename T, typename Context> class arg_converter { template<typename T, typename Context>
private: class arg_converter
{
private:
using char_type = typename Context::char_type; using char_type = typename Context::char_type;
basic_format_arg<Context>& arg_; basic_format_arg<Context> &arg_;
char_type type_; char_type type_;
public: public:
arg_converter(basic_format_arg<Context>& arg, char_type type) arg_converter(basic_format_arg<Context> &arg, char_type type)
: arg_(arg), type_(type) {} : arg_(arg)
, type_(type)
{}
void operator()(bool value) { void operator()(bool value)
if (type_ != 's') operator()<bool>(value); {
if (type_ != 's')
operator()<bool>(value);
} }
template <typename U, FMT_ENABLE_IF(std::is_integral<U>::value)> template<typename U, FMT_ENABLE_IF(std::is_integral<U>::value)>
void operator()(U value) { void operator()(U value)
{
bool is_signed = type_ == 'd' || type_ == 'i'; bool is_signed = type_ == 'd' || type_ == 'i';
using target_type = conditional_t<std::is_same<T, void>::value, U, T>; using target_type = conditional_t<std::is_same<T, void>::value, U, T>;
if (const_check(sizeof(target_type) <= sizeof(int))) { if (const_check(sizeof(target_type) <= sizeof(int)))
{
// Extra casts are used to silence warnings. // Extra casts are used to silence warnings.
if (is_signed) { if (is_signed)
arg_ = internal::make_arg<Context>( {
static_cast<int>(static_cast<target_type>(value))); arg_ = internal::make_arg<Context>(static_cast<int>(static_cast<target_type>(value)));
} else {
using unsigned_type = typename make_unsigned_or_bool<target_type>::type;
arg_ = internal::make_arg<Context>(
static_cast<unsigned>(static_cast<unsigned_type>(value)));
} }
} else { else
if (is_signed) { {
using unsigned_type = typename make_unsigned_or_bool<target_type>::type;
arg_ = internal::make_arg<Context>(static_cast<unsigned>(static_cast<unsigned_type>(value)));
}
}
else
{
if (is_signed)
{
// glibc's printf doesn't sign extend arguments of smaller types: // glibc's printf doesn't sign extend arguments of smaller types:
// std::printf("%lld", -42); // prints "4294967254" // std::printf("%lld", -42); // prints "4294967254"
// but we don't have to do the same because it's a UB. // but we don't have to do the same because it's a UB.
arg_ = internal::make_arg<Context>(static_cast<long long>(value)); arg_ = internal::make_arg<Context>(static_cast<long long>(value));
} else { }
arg_ = internal::make_arg<Context>( else
static_cast<typename make_unsigned_or_bool<U>::type>(value)); {
arg_ = internal::make_arg<Context>(static_cast<typename make_unsigned_or_bool<U>::type>(value));
} }
} }
} }
template <typename U, FMT_ENABLE_IF(!std::is_integral<U>::value)> template<typename U, FMT_ENABLE_IF(!std::is_integral<U>::value)>
void operator()(U) {} // No conversion needed for non-integral types. void operator()(U)
{} // No conversion needed for non-integral types.
}; };
// Converts an integer argument to T for printf, if T is an integral type. // Converts an integer argument to T for printf, if T is an integral type.
// If T is void, the argument is converted to corresponding signed or unsigned // If T is void, the argument is converted to corresponding signed or unsigned
// type depending on the type specifier: 'd' and 'i' - signed, other - // type depending on the type specifier: 'd' and 'i' - signed, other -
// unsigned). // unsigned).
template <typename T, typename Context, typename Char> template<typename T, typename Context, typename Char>
void convert_arg(basic_format_arg<Context>& arg, Char type) { void convert_arg(basic_format_arg<Context> &arg, Char type)
{
visit_format_arg(arg_converter<T, Context>(arg, type), arg); visit_format_arg(arg_converter<T, Context>(arg, type), arg);
} }
// Converts an integer argument to char for printf. // Converts an integer argument to char for printf.
template <typename Context> class char_converter { template<typename Context>
private: class char_converter
basic_format_arg<Context>& arg_; {
private:
basic_format_arg<Context> &arg_;
public: public:
explicit char_converter(basic_format_arg<Context>& arg) : arg_(arg) {} explicit char_converter(basic_format_arg<Context> &arg)
: arg_(arg)
{}
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)> template<typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
void operator()(T value) { void operator()(T value)
arg_ = internal::make_arg<Context>( {
static_cast<typename Context::char_type>(value)); arg_ = internal::make_arg<Context>(static_cast<typename Context::char_type>(value));
} }
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)> template<typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
void operator()(T) {} // No conversion needed for non-integral types. void operator()(T)
{} // No conversion needed for non-integral types.
}; };
// Checks if an argument is a valid printf width specifier and sets // Checks if an argument is a valid printf width specifier and sets
// left alignment if it is negative. // left alignment if it is negative.
template <typename Char> class printf_width_handler { template<typename Char>
private: class printf_width_handler
{
private:
using format_specs = basic_format_specs<Char>; using format_specs = basic_format_specs<Char>;
format_specs& specs_; format_specs &specs_;
public: public:
explicit printf_width_handler(format_specs& specs) : specs_(specs) {} explicit printf_width_handler(format_specs &specs)
: specs_(specs)
{}
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)> template<typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
unsigned operator()(T value) { unsigned operator()(T value)
{
auto width = static_cast<uint32_or_64_or_128_t<T>>(value); auto width = static_cast<uint32_or_64_or_128_t<T>>(value);
if (internal::is_negative(value)) { if (internal::is_negative(value))
{
specs_.align = align::left; specs_.align = align::left;
width = 0 - width; width = 0 - width;
} }
unsigned int_max = max_value<int>(); unsigned int_max = max_value<int>();
if (width > int_max) FMT_THROW(format_error("number is too big")); if (width > int_max)
FMT_THROW(format_error("number is too big"));
return static_cast<unsigned>(width); return static_cast<unsigned>(width);
} }
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)> template<typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
unsigned operator()(T) { unsigned operator()(T)
{
FMT_THROW(format_error("width is not integer")); FMT_THROW(format_error("width is not integer"));
return 0; return 0;
} }
}; };
template <typename Char, typename Context> template<typename Char, typename Context>
void printf(buffer<Char>& buf, basic_string_view<Char> format, void printf(buffer<Char> &buf, basic_string_view<Char> format, basic_format_args<Context> args)
basic_format_args<Context> args) { {
Context(std::back_inserter(buf), format, args).format(); Context(std::back_inserter(buf), format, args).format();
} }
template <typename OutputIt, typename Char, typename Context> template<typename OutputIt, typename Char, typename Context>
internal::truncating_iterator<OutputIt> printf( internal::truncating_iterator<OutputIt> printf(
internal::truncating_iterator<OutputIt> it, basic_string_view<Char> format, internal::truncating_iterator<OutputIt> it, basic_string_view<Char> format, basic_format_args<Context> args)
basic_format_args<Context> args) { {
return Context(it, format, args).format(); return Context(it, format, args).format();
} }
} // namespace internal } // namespace internal
using internal::printf; // For printing into memory_buffer. using internal::printf; // For printing into memory_buffer.
template <typename Range> class printf_arg_formatter; template<typename Range>
class printf_arg_formatter;
template <typename OutputIt, typename Char> class basic_printf_context; template<typename OutputIt, typename Char>
class basic_printf_context;
/** /**
\rst \rst
The ``printf`` argument formatter. The ``printf`` argument formatter.
\endrst \endrst
*/ */
template <typename Range> template<typename Range>
class printf_arg_formatter : public internal::arg_formatter_base<Range> { class printf_arg_formatter : public internal::arg_formatter_base<Range>
public: {
public:
using iterator = typename Range::iterator; using iterator = typename Range::iterator;
private: private:
using char_type = typename Range::value_type; using char_type = typename Range::value_type;
using base = internal::arg_formatter_base<Range>; using base = internal::arg_formatter_base<Range>;
using context_type = basic_printf_context<iterator, char_type>; using context_type = basic_printf_context<iterator, char_type>;
context_type& context_; context_type &context_;
void write_null_pointer(char) { void write_null_pointer(char)
{
this->specs()->type = 0; this->specs()->type = 0;
this->write("(nil)"); this->write("(nil)");
} }
void write_null_pointer(wchar_t) { void write_null_pointer(wchar_t)
{
this->specs()->type = 0; this->specs()->type = 0;
this->write(L"(nil)"); this->write(L"(nil)");
} }
public: public:
using format_specs = typename base::format_specs; using format_specs = typename base::format_specs;
/** /**
@ -228,39 +286,50 @@ class printf_arg_formatter : public internal::arg_formatter_base<Range> {
specifier information for standard argument types. specifier information for standard argument types.
\endrst \endrst
*/ */
printf_arg_formatter(iterator iter, format_specs& specs, context_type& ctx) printf_arg_formatter(iterator iter, format_specs &specs, context_type &ctx)
: base(Range(iter), &specs, internal::locale_ref()), context_(ctx) {} : base(Range(iter), &specs, internal::locale_ref())
, context_(ctx)
{}
template <typename T, FMT_ENABLE_IF(fmt::internal::is_integral<T>::value)> template<typename T, FMT_ENABLE_IF(fmt::internal::is_integral<T>::value)>
iterator operator()(T value) { iterator operator()(T value)
{
// MSVC2013 fails to compile separate overloads for bool and char_type so // MSVC2013 fails to compile separate overloads for bool and char_type so
// use std::is_same instead. // use std::is_same instead.
if (std::is_same<T, bool>::value) { if (std::is_same<T, bool>::value)
format_specs& fmt_specs = *this->specs(); {
if (fmt_specs.type != 's') return base::operator()(value ? 1 : 0); format_specs &fmt_specs = *this->specs();
if (fmt_specs.type != 's')
return base::operator()(value ? 1 : 0);
fmt_specs.type = 0; fmt_specs.type = 0;
this->write(value != 0); this->write(value != 0);
} else if (std::is_same<T, char_type>::value) { }
format_specs& fmt_specs = *this->specs(); else if (std::is_same<T, char_type>::value)
{
format_specs &fmt_specs = *this->specs();
if (fmt_specs.type && fmt_specs.type != 'c') if (fmt_specs.type && fmt_specs.type != 'c')
return (*this)(static_cast<int>(value)); return (*this)(static_cast<int>(value));
fmt_specs.sign = sign::none; fmt_specs.sign = sign::none;
fmt_specs.alt = false; fmt_specs.alt = false;
fmt_specs.align = align::right; fmt_specs.align = align::right;
return base::operator()(value); return base::operator()(value);
} else { }
else
{
return base::operator()(value); return base::operator()(value);
} }
return this->out(); return this->out();
} }
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)> template<typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
iterator operator()(T value) { iterator operator()(T value)
{
return base::operator()(value); return base::operator()(value);
} }
/** Formats a null-terminated C string. */ /** Formats a null-terminated C string. */
iterator operator()(const char* value) { iterator operator()(const char *value)
{
if (value) if (value)
base::operator()(value); base::operator()(value);
else if (this->specs()->type == 'p') else if (this->specs()->type == 'p')
@ -271,7 +340,8 @@ class printf_arg_formatter : public internal::arg_formatter_base<Range> {
} }
/** Formats a null-terminated wide C string. */ /** Formats a null-terminated wide C string. */
iterator operator()(const wchar_t* value) { iterator operator()(const wchar_t *value)
{
if (value) if (value)
base::operator()(value); base::operator()(value);
else if (this->specs()->type == 'p') else if (this->specs()->type == 'p')
@ -281,66 +351,79 @@ class printf_arg_formatter : public internal::arg_formatter_base<Range> {
return this->out(); return this->out();
} }
iterator operator()(basic_string_view<char_type> value) { iterator operator()(basic_string_view<char_type> value)
{
return base::operator()(value); return base::operator()(value);
} }
iterator operator()(monostate value) { return base::operator()(value); } iterator operator()(monostate value)
{
return base::operator()(value);
}
/** Formats a pointer. */ /** Formats a pointer. */
iterator operator()(const void* value) { iterator operator()(const void *value)
if (value) return base::operator()(value); {
if (value)
return base::operator()(value);
this->specs()->type = 0; this->specs()->type = 0;
write_null_pointer(char_type()); write_null_pointer(char_type());
return this->out(); return this->out();
} }
/** Formats an argument of a custom (user-defined) type. */ /** Formats an argument of a custom (user-defined) type. */
iterator operator()(typename basic_format_arg<context_type>::handle handle) { iterator operator()(typename basic_format_arg<context_type>::handle handle)
{
handle.format(context_.parse_context(), context_); handle.format(context_.parse_context(), context_);
return this->out(); return this->out();
} }
}; };
template <typename T> struct printf_formatter { template<typename T>
template <typename ParseContext> struct printf_formatter
auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { {
template<typename ParseContext>
auto parse(ParseContext &ctx) -> decltype(ctx.begin())
{
return ctx.begin(); return ctx.begin();
} }
template <typename FormatContext> template<typename FormatContext>
auto format(const T& value, FormatContext& ctx) -> decltype(ctx.out()) { auto format(const T &value, FormatContext &ctx) -> decltype(ctx.out())
{
internal::format_value(internal::get_container(ctx.out()), value); internal::format_value(internal::get_container(ctx.out()), value);
return ctx.out(); return ctx.out();
} }
}; };
/** This template formats data and writes the output to a writer. */ /** This template formats data and writes the output to a writer. */
template <typename OutputIt, typename Char> class basic_printf_context { template<typename OutputIt, typename Char>
public: class basic_printf_context
{
public:
/** The character type for the output. */ /** The character type for the output. */
using char_type = Char; using char_type = Char;
using format_arg = basic_format_arg<basic_printf_context>; using format_arg = basic_format_arg<basic_printf_context>;
template <typename T> using formatter_type = printf_formatter<T>; template<typename T>
using formatter_type = printf_formatter<T>;
private: private:
using format_specs = basic_format_specs<char_type>; using format_specs = basic_format_specs<char_type>;
OutputIt out_; OutputIt out_;
basic_format_args<basic_printf_context> args_; basic_format_args<basic_printf_context> args_;
basic_format_parse_context<Char> parse_ctx_; basic_format_parse_context<Char> parse_ctx_;
static void parse_flags(format_specs& specs, const Char*& it, static void parse_flags(format_specs &specs, const Char *&it, const Char *end);
const Char* end);
// Returns the argument with specified index or, if arg_index is -1, the next // Returns the argument with specified index or, if arg_index is -1, the next
// argument. // argument.
format_arg get_arg(int arg_index = -1); format_arg get_arg(int arg_index = -1);
// Parses argument index, flags and width and returns the argument index. // Parses argument index, flags and width and returns the argument index.
int parse_header(const Char*& it, const Char* end, format_specs& specs); int parse_header(const Char *&it, const Char *end, format_specs &specs);
public: public:
/** /**
\rst \rst
Constructs a ``printf_context`` object. References to the arguments and Constructs a ``printf_context`` object. References to the arguments and
@ -348,32 +431,48 @@ template <typename OutputIt, typename Char> class basic_printf_context {
appropriate lifetimes. appropriate lifetimes.
\endrst \endrst
*/ */
basic_printf_context(OutputIt out, basic_string_view<char_type> format_str, basic_printf_context(OutputIt out, basic_string_view<char_type> format_str, basic_format_args<basic_printf_context> args)
basic_format_args<basic_printf_context> args) : out_(out)
: out_(out), args_(args), parse_ctx_(format_str) {} , args_(args)
, parse_ctx_(format_str)
{}
OutputIt out() { return out_; } OutputIt out()
void advance_to(OutputIt it) { out_ = it; } {
return out_;
}
void advance_to(OutputIt it)
{
out_ = it;
}
format_arg arg(int id) const { return args_.get(id); } format_arg arg(int id) const
{
return args_.get(id);
}
basic_format_parse_context<Char>& parse_context() { return parse_ctx_; } basic_format_parse_context<Char> &parse_context()
{
return parse_ctx_;
}
FMT_CONSTEXPR void on_error(const char* message) { FMT_CONSTEXPR void on_error(const char *message)
{
parse_ctx_.on_error(message); parse_ctx_.on_error(message);
} }
/** Formats stored arguments and writes the output to the range. */ /** Formats stored arguments and writes the output to the range. */
template <typename ArgFormatter = printf_arg_formatter<buffer_range<Char>>> template<typename ArgFormatter = printf_arg_formatter<buffer_range<Char>>>
OutputIt format(); OutputIt format();
}; };
template <typename OutputIt, typename Char> template<typename OutputIt, typename Char>
void basic_printf_context<OutputIt, Char>::parse_flags(format_specs& specs, void basic_printf_context<OutputIt, Char>::parse_flags(format_specs &specs, const Char *&it, const Char *end)
const Char*& it, {
const Char* end) { for (; it != end; ++it)
for (; it != end; ++it) { {
switch (*it) { switch (*it)
{
case '-': case '-':
specs.align = align::left; specs.align = align::left;
break; break;
@ -395,9 +494,9 @@ void basic_printf_context<OutputIt, Char>::parse_flags(format_specs& specs,
} }
} }
template <typename OutputIt, typename Char> template<typename OutputIt, typename Char>
typename basic_printf_context<OutputIt, Char>::format_arg typename basic_printf_context<OutputIt, Char>::format_arg basic_printf_context<OutputIt, Char>::get_arg(int arg_index)
basic_printf_context<OutputIt, Char>::get_arg(int arg_index) { {
if (arg_index < 0) if (arg_index < 0)
arg_index = parse_ctx_.next_arg_id(); arg_index = parse_ctx_.next_arg_id();
else else
@ -405,22 +504,28 @@ basic_printf_context<OutputIt, Char>::get_arg(int arg_index) {
return internal::get_arg(*this, arg_index); return internal::get_arg(*this, arg_index);
} }
template <typename OutputIt, typename Char> template<typename OutputIt, typename Char>
int basic_printf_context<OutputIt, Char>::parse_header( int basic_printf_context<OutputIt, Char>::parse_header(const Char *&it, const Char *end, format_specs &specs)
const Char*& it, const Char* end, format_specs& specs) { {
int arg_index = -1; int arg_index = -1;
char_type c = *it; char_type c = *it;
if (c >= '0' && c <= '9') { if (c >= '0' && c <= '9')
{
// Parse an argument index (if followed by '$') or a width possibly // Parse an argument index (if followed by '$') or a width possibly
// preceded with '0' flag(s). // preceded with '0' flag(s).
internal::error_handler eh; internal::error_handler eh;
int value = parse_nonnegative_int(it, end, eh); int value = parse_nonnegative_int(it, end, eh);
if (it != end && *it == '$') { // value is an argument index if (it != end && *it == '$')
{ // value is an argument index
++it; ++it;
arg_index = value; arg_index = value;
} else { }
if (c == '0') specs.fill[0] = '0'; else
if (value != 0) { {
if (c == '0')
specs.fill[0] = '0';
if (value != 0)
{
// Nonzero value means that we parsed width and don't need to // Nonzero value means that we parsed width and don't need to
// parse it or flags again, so return now. // parse it or flags again, so return now.
specs.width = value; specs.width = value;
@ -430,30 +535,37 @@ int basic_printf_context<OutputIt, Char>::parse_header(
} }
parse_flags(specs, it, end); parse_flags(specs, it, end);
// Parse width. // Parse width.
if (it != end) { if (it != end)
if (*it >= '0' && *it <= '9') { {
if (*it >= '0' && *it <= '9')
{
internal::error_handler eh; internal::error_handler eh;
specs.width = parse_nonnegative_int(it, end, eh); specs.width = parse_nonnegative_int(it, end, eh);
} else if (*it == '*') { }
else if (*it == '*')
{
++it; ++it;
specs.width = static_cast<int>(visit_format_arg( specs.width = static_cast<int>(visit_format_arg(internal::printf_width_handler<char_type>(specs), get_arg()));
internal::printf_width_handler<char_type>(specs), get_arg()));
} }
} }
return arg_index; return arg_index;
} }
template <typename OutputIt, typename Char> template<typename OutputIt, typename Char>
template <typename ArgFormatter> template<typename ArgFormatter>
OutputIt basic_printf_context<OutputIt, Char>::format() { OutputIt basic_printf_context<OutputIt, Char>::format()
{
auto out = this->out(); auto out = this->out();
const Char* start = parse_ctx_.begin(); const Char *start = parse_ctx_.begin();
const Char* end = parse_ctx_.end(); const Char *end = parse_ctx_.end();
auto it = start; auto it = start;
while (it != end) { while (it != end)
{
char_type c = *it++; char_type c = *it++;
if (c != '%') continue; if (c != '%')
if (it != end && *it == c) { continue;
if (it != end && *it == c)
{
out = std::copy(start, it, out); out = std::copy(start, it, out);
start = ++it; start = ++it;
continue; continue;
@ -465,20 +577,26 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
// Parse argument index, flags and width. // Parse argument index, flags and width.
int arg_index = parse_header(it, end, specs); int arg_index = parse_header(it, end, specs);
if (arg_index == 0) on_error("argument index out of range"); if (arg_index == 0)
on_error("argument index out of range");
// Parse precision. // Parse precision.
if (it != end && *it == '.') { if (it != end && *it == '.')
{
++it; ++it;
c = it != end ? *it : 0; c = it != end ? *it : 0;
if ('0' <= c && c <= '9') { if ('0' <= c && c <= '9')
{
internal::error_handler eh; internal::error_handler eh;
specs.precision = parse_nonnegative_int(it, end, eh); specs.precision = parse_nonnegative_int(it, end, eh);
} else if (c == '*') { }
else if (c == '*')
{
++it; ++it;
specs.precision = specs.precision = static_cast<int>(visit_format_arg(internal::printf_precision_handler(), get_arg()));
static_cast<int>(visit_format_arg(internal::printf_precision_handler(), get_arg())); }
} else { else
{
specs.precision = 0; specs.precision = 0;
} }
} }
@ -486,7 +604,8 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
format_arg arg = get_arg(arg_index); format_arg arg = get_arg(arg_index);
if (specs.alt && visit_format_arg(internal::is_zero_int(), arg)) if (specs.alt && visit_format_arg(internal::is_zero_int(), arg))
specs.alt = false; specs.alt = false;
if (specs.fill[0] == '0') { if (specs.fill[0] == '0')
{
if (arg.is_arithmetic()) if (arg.is_arithmetic())
specs.align = align::numeric; specs.align = align::numeric;
else else
@ -497,22 +616,29 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
c = it != end ? *it++ : 0; c = it != end ? *it++ : 0;
char_type t = it != end ? *it : 0; char_type t = it != end ? *it : 0;
using internal::convert_arg; using internal::convert_arg;
switch (c) { switch (c)
{
case 'h': case 'h':
if (t == 'h') { if (t == 'h')
{
++it; ++it;
t = it != end ? *it : 0; t = it != end ? *it : 0;
convert_arg<signed char>(arg, t); convert_arg<signed char>(arg, t);
} else { }
else
{
convert_arg<short>(arg, t); convert_arg<short>(arg, t);
} }
break; break;
case 'l': case 'l':
if (t == 'l') { if (t == 'l')
{
++it; ++it;
t = it != end ? *it : 0; t = it != end ? *it : 0;
convert_arg<long long>(arg, t); convert_arg<long long>(arg, t);
} else { }
else
{
convert_arg<long>(arg, t); convert_arg<long>(arg, t);
} }
break; break;
@ -535,18 +661,20 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
} }
// Parse type. // Parse type.
if (it == end) FMT_THROW(format_error("invalid format string")); if (it == end)
FMT_THROW(format_error("invalid format string"));
specs.type = static_cast<char>(*it++); specs.type = static_cast<char>(*it++);
if (arg.is_integral()) { if (arg.is_integral())
{
// Normalize type. // Normalize type.
switch (specs.type) { switch (specs.type)
{
case 'i': case 'i':
case 'u': case 'u':
specs.type = 'd'; specs.type = 'd';
break; break;
case 'c': case 'c':
visit_format_arg(internal::char_converter<basic_printf_context>(arg), visit_format_arg(internal::char_converter<basic_printf_context>(arg), arg);
arg);
break; break;
} }
} }
@ -559,10 +687,8 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
return std::copy(start, it, out); return std::copy(start, it, out);
} }
template <typename Char> template<typename Char>
using basic_printf_context_t = using basic_printf_context_t = basic_printf_context<std::back_insert_iterator<internal::buffer<Char>>, Char>;
basic_printf_context<std::back_insert_iterator<internal::buffer<Char>>,
Char>;
using printf_context = basic_printf_context_t<char>; using printf_context = basic_printf_context_t<char>;
using wprintf_context = basic_printf_context_t<wchar_t>; using wprintf_context = basic_printf_context_t<wchar_t>;
@ -576,9 +702,9 @@ using wprintf_args = basic_format_args<wprintf_context>;
arguments and can be implicitly converted to `~fmt::printf_args`. arguments and can be implicitly converted to `~fmt::printf_args`.
\endrst \endrst
*/ */
template <typename... Args> template<typename... Args>
inline format_arg_store<printf_context, Args...> make_printf_args( inline format_arg_store<printf_context, Args...> make_printf_args(const Args &... args)
const Args&... args) { {
return {args...}; return {args...};
} }
@ -588,15 +714,15 @@ inline format_arg_store<printf_context, Args...> make_printf_args(
arguments and can be implicitly converted to `~fmt::wprintf_args`. arguments and can be implicitly converted to `~fmt::wprintf_args`.
\endrst \endrst
*/ */
template <typename... Args> template<typename... Args>
inline format_arg_store<wprintf_context, Args...> make_wprintf_args( inline format_arg_store<wprintf_context, Args...> make_wprintf_args(const Args &... args)
const Args&... args) { {
return {args...}; return {args...};
} }
template <typename S, typename Char = char_t<S>> template<typename S, typename Char = char_t<S>>
inline std::basic_string<Char> vsprintf( inline std::basic_string<Char> vsprintf(const S &format, basic_format_args<basic_printf_context_t<Char>> args)
const S& format, basic_format_args<basic_printf_context_t<Char>> args) { {
basic_memory_buffer<Char> buffer; basic_memory_buffer<Char> buffer;
printf(buffer, to_string_view(format), args); printf(buffer, to_string_view(format), args);
return to_string(buffer); return to_string(buffer);
@ -611,22 +737,20 @@ inline std::basic_string<Char> vsprintf(
std::string message = fmt::sprintf("The answer is %d", 42); std::string message = fmt::sprintf("The answer is %d", 42);
\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>>> inline std::basic_string<Char> sprintf(const S &format, const Args &... args)
inline std::basic_string<Char> sprintf(const S& format, const Args&... args) { {
using context = basic_printf_context_t<Char>; using context = basic_printf_context_t<Char>;
return vsprintf(to_string_view(format), {make_format_args<context>(args...)}); return vsprintf(to_string_view(format), {make_format_args<context>(args...)});
} }
template <typename S, typename Char = char_t<S>> template<typename S, typename Char = char_t<S>>
inline int vfprintf(std::FILE* f, const S& format, inline int vfprintf(std::FILE *f, const S &format, basic_format_args<basic_printf_context_t<Char>> args)
basic_format_args<basic_printf_context_t<Char>> args) { {
basic_memory_buffer<Char> buffer; basic_memory_buffer<Char> buffer;
printf(buffer, to_string_view(format), args); printf(buffer, to_string_view(format), args);
std::size_t size = buffer.size(); std::size_t size = buffer.size();
return std::fwrite(buffer.data(), sizeof(Char), size, f) < size return std::fwrite(buffer.data(), sizeof(Char), size, f) < size ? -1 : static_cast<int>(size);
? -1
: static_cast<int>(size);
} }
/** /**
@ -638,17 +762,16 @@ inline int vfprintf(std::FILE* f, const S& format,
fmt::fprintf(stderr, "Don't %s!", "panic"); fmt::fprintf(stderr, "Don't %s!", "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>>> inline int fprintf(std::FILE *f, const S &format, const Args &... args)
inline int fprintf(std::FILE* f, const S& format, const Args&... args) { {
using context = basic_printf_context_t<Char>; using context = basic_printf_context_t<Char>;
return vfprintf(f, to_string_view(format), return vfprintf(f, to_string_view(format), {make_format_args<context>(args...)});
{make_format_args<context>(args...)});
} }
template <typename S, typename Char = char_t<S>> template<typename S, typename Char = char_t<S>>
inline int vprintf(const S& format, inline int vprintf(const S &format, basic_format_args<basic_printf_context_t<Char>> args)
basic_format_args<basic_printf_context_t<Char>> args) { {
return vfprintf(stdout, to_string_view(format), args); return vfprintf(stdout, to_string_view(format), args);
} }
@ -661,17 +784,16 @@ inline int vprintf(const S& format,
fmt::printf("Elapsed time: %.2f seconds", 1.23); fmt::printf("Elapsed time: %.2f seconds", 1.23);
\endrst \endrst
*/ */
template <typename S, typename... Args, template<typename S, typename... Args, FMT_ENABLE_IF(internal::is_string<S>::value)>
FMT_ENABLE_IF(internal::is_string<S>::value)> inline int printf(const S &format_str, const Args &... args)
inline int printf(const S& format_str, const Args&... args) { {
using context = basic_printf_context_t<char_t<S>>; using context = basic_printf_context_t<char_t<S>>;
return vprintf(to_string_view(format_str), return vprintf(to_string_view(format_str), {make_format_args<context>(args...)});
{make_format_args<context>(args...)});
} }
template <typename S, typename Char = char_t<S>> template<typename S, typename Char = char_t<S>>
inline int vfprintf(std::basic_ostream<Char>& os, const S& format, inline int vfprintf(std::basic_ostream<Char> &os, const S &format, basic_format_args<basic_printf_context_t<Char>> args)
basic_format_args<basic_printf_context_t<Char>> args) { {
basic_memory_buffer<Char> buffer; basic_memory_buffer<Char> buffer;
printf(buffer, to_string_view(format), args); printf(buffer, to_string_view(format), args);
internal::write(os, buffer); internal::write(os, buffer);
@ -679,12 +801,9 @@ inline int vfprintf(std::basic_ostream<Char>& os, const S& format,
} }
/** Formats arguments and writes the output to the range. */ /** Formats arguments and writes the output to the range. */
template <typename ArgFormatter, typename Char, template<typename ArgFormatter, typename Char, typename Context = basic_printf_context<typename ArgFormatter::iterator, Char>>
typename Context = typename ArgFormatter::iterator vprintf(internal::buffer<Char> &out, basic_string_view<Char> format_str, basic_format_args<Context> args)
basic_printf_context<typename ArgFormatter::iterator, Char>> {
typename ArgFormatter::iterator vprintf(internal::buffer<Char>& out,
basic_string_view<Char> format_str,
basic_format_args<Context> args) {
typename ArgFormatter::iterator iter(out); typename ArgFormatter::iterator iter(out);
Context(iter, format_str, args).template format<ArgFormatter>(); Context(iter, format_str, args).template format<ArgFormatter>();
return iter; return iter;
@ -699,12 +818,11 @@ typename ArgFormatter::iterator vprintf(internal::buffer<Char>& out,
fmt::fprintf(cerr, "Don't %s!", "panic"); fmt::fprintf(cerr, "Don't %s!", "panic");
\endrst \endrst
*/ */
template <typename S, typename... Args, typename Char = char_t<S>> template<typename S, typename... Args, typename Char = char_t<S>>
inline int fprintf(std::basic_ostream<Char>& os, const S& format_str, inline int fprintf(std::basic_ostream<Char> &os, const S &format_str, const Args &... args)
const Args&... args) { {
using context = basic_printf_context_t<Char>; using context = basic_printf_context_t<Char>;
return vfprintf(os, to_string_view(format_str), return vfprintf(os, to_string_view(format_str), {make_format_args<context>(args...)});
{make_format_args<context>(args...)});
} }
FMT_END_NAMESPACE FMT_END_NAMESPACE

View File

@ -17,223 +17,264 @@
// 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()) { {
template<typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin())
{
return 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()
: prefix('{')
, delimiter(',')
, postfix('}')
{}
static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true; static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true;
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false; 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 prefix;
Char delimiter; Char delimiter;
Char postfix; Char postfix;
formatting_tuple() : prefix('('), delimiter(','), postfix(')') {} formatting_tuple()
: prefix('(')
, delimiter(',')
, postfix(')')
{}
static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true; static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true;
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false; 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) for (auto it = range.begin(), end = range.end(); it != end; ++it)
*out++ = *it; *out++ = *it;
return out; 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++; {
while (*str)
*out++ = *str++;
return out; return out;
} }
template <typename OutputIterator> template<typename OutputIterator>
OutputIterator copy(char ch, OutputIterator out) { OutputIterator copy(char ch, OutputIterator out)
{
*out++ = ch; *out++ = ch;
return out; 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>
struct integer_sequence
{
using value_type = T; 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 std::get;
// using free function get<I>(T) now. // using free function get<I>(T) now.
const int _[] = {0, ((void)f(get<Is>(tup)), 0)...}; const int _[] = {0, ((void)f(get<Is>(tup)), 0)...};
(void)_; // blocks warnings (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>
void for_each(Tuple &&tup, F &&f)
{
const auto indexes = get_indexes(tup); const auto indexes = get_indexes(tup);
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f)); 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: {
private:
// C++11 generic lambda for format() // C++11 generic lambda for format()
template <typename FormatContext> struct format_each { template<typename FormatContext>
template <typename T> void operator()(const T& v) { struct format_each
if (i > 0) { {
if (formatting.add_prepostfix_space) { template<typename T>
void operator()(const T &v)
{
if (i > 0)
{
if (formatting.add_prepostfix_space)
{
*out++ = ' '; *out++ = ' ';
} }
out = internal::copy(formatting.delimiter, out); out = internal::copy(formatting.delimiter, out);
} }
out = format_to(out, out = format_to(out, internal::format_str_quoted((formatting.add_delimiter_spaces && i > 0), v), v);
internal::format_str_quoted(
(formatting.add_delimiter_spaces && i > 0), v),
v);
++i; ++i;
} }
formatting_tuple<Char>& formatting; formatting_tuple<Char> &formatting;
std::size_t& i; std::size_t &i;
typename std::add_lvalue_reference<decltype( typename std::add_lvalue_reference<decltype(std::declval<FormatContext>().out())>::type out;
std::declval<FormatContext>().out())>::type out;
}; };
public: public:
formatting_tuple<Char> formatting; formatting_tuple<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 = format_context> template<typename FormatContext = format_context>
auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) { auto format(const TupleT &values, FormatContext &ctx) -> decltype(ctx.out())
{
auto out = ctx.out(); auto out = ctx.out();
std::size_t i = 0; std::size_t i = 0;
internal::copy(formatting.prefix, out); internal::copy(formatting.prefix, out);
internal::for_each(values, format_each<FormatContext>{formatting, i, out}); internal::for_each(values, format_each<FormatContext>{formatting, i, out});
if (formatting.add_prepostfix_space) { if (formatting.add_prepostfix_space)
{
*out++ = ' '; *out++ = ' ';
} }
internal::copy(formatting.postfix, out); internal::copy(formatting.postfix, out);
@ -242,93 +283,102 @@ struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
} }
}; };
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> template<typename FormatContext>
typename FormatContext::iterator format(const RangeT& values, typename FormatContext::iterator format(const RangeT &values, FormatContext &ctx)
FormatContext& ctx) { {
auto out = internal::copy(formatting.prefix, ctx.out()); auto out = internal::copy(formatting.prefix, ctx.out());
std::size_t i = 0; std::size_t i = 0;
for (auto it = values.begin(), end = values.end(); it != end; ++it) { for (auto it = values.begin(), end = values.end(); it != end; ++it)
if (i > 0) { {
if (formatting.add_prepostfix_space) *out++ = ' '; if (i > 0)
{
if (formatting.add_prepostfix_space)
*out++ = ' ';
out = internal::copy(formatting.delimiter, out); out = internal::copy(formatting.delimiter, out);
} }
out = format_to(out, out = format_to(out, internal::format_str_quoted((formatting.add_delimiter_spaces && i > 0), *it), *it);
internal::format_str_quoted( if (++i > formatting.range_length_limit)
(formatting.add_delimiter_spaces && i > 0), *it), {
*it);
if (++i > formatting.range_length_limit) {
out = format_to(out, " ... <other elements>"); out = format_to(out, " ... <other elements>");
break; break;
} }
} }
if (formatting.add_prepostfix_space) *out++ = ' '; if (formatting.add_prepostfix_space)
*out++ = ' ';
return internal::copy(formatting.postfix, out); return internal::copy(formatting.postfix, out);
} }
}; };
template <typename Char, typename... T> struct tuple_arg_join : internal::view { template<typename Char, typename... T>
const std::tuple<T...>& tuple; struct tuple_arg_join : internal::view
{
const std::tuple<T...> &tuple;
basic_string_view<Char> sep; basic_string_view<Char> sep;
tuple_arg_join(const std::tuple<T...>& t, basic_string_view<Char> s) tuple_arg_join(const std::tuple<T...> &t, basic_string_view<Char> s)
: tuple{t}, sep{s} {} : tuple{t}
, sep{s}
{}
}; };
template <typename Char, typename... T> template<typename Char, typename... T>
struct formatter<tuple_arg_join<Char, T...>, Char> { struct formatter<tuple_arg_join<Char, T...>, Char>
template <typename ParseContext> {
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { template<typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin())
{
return ctx.begin(); return ctx.begin();
} }
template <typename FormatContext> template<typename FormatContext>
typename FormatContext::iterator format( typename FormatContext::iterator format(const tuple_arg_join<Char, T...> &value, FormatContext &ctx)
const tuple_arg_join<Char, T...>& value, FormatContext& ctx) { {
return format(value, ctx, internal::make_index_sequence<sizeof...(T)>{}); return format(value, ctx, internal::make_index_sequence<sizeof...(T)>{});
} }
private: private:
template <typename FormatContext, size_t... N> template<typename FormatContext, size_t... N>
typename FormatContext::iterator format( typename FormatContext::iterator format(const tuple_arg_join<Char, T...> &value, FormatContext &ctx, internal::index_sequence<N...>)
const tuple_arg_join<Char, T...>& value, FormatContext& ctx, {
internal::index_sequence<N...>) {
return format_args(value, ctx, std::get<N>(value.tuple)...); return format_args(value, ctx, std::get<N>(value.tuple)...);
} }
template <typename FormatContext> template<typename FormatContext>
typename FormatContext::iterator format_args( typename FormatContext::iterator format_args(const tuple_arg_join<Char, T...> &, FormatContext &ctx)
const tuple_arg_join<Char, T...>&, FormatContext& ctx) { {
// NOTE: for compilers that support C++17, this empty function instantiation // NOTE: for compilers that support C++17, this empty function instantiation
// can be replaced with a constexpr branch in the variadic overload. // can be replaced with a constexpr branch in the variadic overload.
return ctx.out(); return ctx.out();
} }
template <typename FormatContext, typename Arg, typename... Args> template<typename FormatContext, typename Arg, typename... Args>
typename FormatContext::iterator format_args( typename FormatContext::iterator format_args(
const tuple_arg_join<Char, T...>& value, FormatContext& ctx, const tuple_arg_join<Char, T...> &value, FormatContext &ctx, const Arg &arg, const Args &... args)
const Arg& arg, const Args&... args) { {
using base = formatter<typename std::decay<Arg>::type, Char>; using base = formatter<typename std::decay<Arg>::type, Char>;
auto out = ctx.out(); auto out = ctx.out();
out = base{}.format(arg, ctx); out = base{}.format(arg, ctx);
if (sizeof...(Args) > 0) { if (sizeof...(Args) > 0)
{
out = std::copy(value.sep.begin(), value.sep.end(), out); out = std::copy(value.sep.begin(), value.sep.end(), out);
ctx.advance_to(out); ctx.advance_to(out);
return format_args(value, ctx, args...); return format_args(value, ctx, args...);
@ -348,15 +398,15 @@ 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};
} }