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: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/pocc/pre-commit-hooks
rev: master
hooks:
- id: clang-format
args: [-i, -style=file]

View File

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

View File

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

View File

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

View File

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

View File

@ -5,10 +5,10 @@
*/
#include "IXCobraToStatsdBot.h"
#include "IXCobraBot.h"
#include "IXQueueManager.h"
#include "IXStatsdClient.h"
#include "IXCobraBot.h"
#include <chrono>
#include <ixcobra/IXCobraConnection.h>
#include <spdlog/spdlog.h>
@ -74,7 +74,8 @@ namespace ix
auto tokens = parseFields(fields);
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 bool verbose,
std::atomic<bool>& /*throttled*/,

View File

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

View File

@ -5,13 +5,13 @@
*/
#include "IXCobraToStdoutBot.h"
#include "IXCobraBot.h"
#include "IXQueueManager.h"
#include <chrono>
#include <iostream>
#include <spdlog/spdlog.h>
#include <sstream>
#include <iostream>
namespace ix
{
@ -79,11 +79,13 @@ namespace ix
CobraBot bot;
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 bool /*verbose*/,
std::atomic<bool>& /*throttled*/,
std::atomic<bool>& /*fatalCobraError*/) -> bool {
std::atomic<bool> &
/*fatalCobraError*/) -> bool {
if (!quiet)
{
writeToStdout(fluentd, jsonWriter, msg, position);

View File

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

View File

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

View File

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

View File

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

View File

@ -6,13 +6,12 @@
#pragma once
#include <atomic>
#include <deque>
#include <ixwebsocket/IXUdpSocket.h>
#include <mutex>
#include <string>
#include <thread>
#include <deque>
#include <mutex>
#include <atomic>
namespace ix
{

View File

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

View File

@ -5,17 +5,17 @@
*/
#include "IXCobraConnection.h"
#include <ixcrypto/IXHMac.h>
#include <ixwebsocket/IXWebSocket.h>
#include <ixwebsocket/IXSocketTLSOptions.h>
#include <algorithm>
#include <stdexcept>
#include <cmath>
#include <cassert>
#include <cmath>
#include <cstring>
#include <iostream>
#include <ixcrypto/IXHMac.h>
#include <ixwebsocket/IXSocketTLSOptions.h>
#include <ixwebsocket/IXWebSocket.h>
#include <sstream>
#include <stdexcept>
namespace ix
@ -26,12 +26,12 @@ namespace ix
constexpr CobraConnection::MsgId CobraConnection::kInvalidMsgId;
constexpr int CobraConnection::kPingIntervalSecs;
CobraConnection::CobraConnection() :
_webSocket(new WebSocket()),
_publishMode(CobraConnection_PublishMode_Immediate),
_authenticated(false),
_eventCallback(nullptr),
_id(1)
CobraConnection::CobraConnection()
: _webSocket(new WebSocket())
, _publishMode(CobraConnection_PublishMode_Immediate)
, _authenticated(false)
, _eventCallback(nullptr)
, _id(1)
{
_pdu["action"] = "rtm/publish";
@ -97,11 +97,7 @@ namespace ix
if (_eventCallback)
{
_eventCallback(
std::make_unique<CobraEvent>(eventType,
errorMsg,
headers,
subscriptionId,
msgId));
std::make_unique<CobraEvent>(eventType, errorMsg, headers, subscriptionId, msgId));
}
}
@ -121,17 +117,13 @@ namespace ix
void CobraConnection::initWebSocketOnMessageCallback()
{
_webSocket->setOnMessageCallback(
[this](const ix::WebSocketMessagePtr& msg)
{
_webSocket->setOnMessageCallback([this](const ix::WebSocketMessagePtr& msg) {
CobraConnection::invokeTrafficTrackerCallback(msg->wireSize, true);
std::stringstream ss;
if (msg->type == ix::WebSocketMessageType::Open)
{
invokeEventCallback(ix::CobraEventType::Open,
std::string(),
msg->openInfo.headers);
invokeEventCallback(ix::CobraEventType::Open, std::string(), msg->openInfo.headers);
sendHandshakeMessage();
}
else if (msg->type == ix::WebSocketMessageType::Close)
@ -141,8 +133,7 @@ namespace ix
std::stringstream ss;
ss << "Close code " << msg->closeInfo.code;
ss << " reason " << msg->closeInfo.reason;
invokeEventCallback(ix::CobraEventType::Closed,
ss.str());
invokeEventCallback(ix::CobraEventType::Closed, ss.str());
}
else if (msg->type == ix::WebSocketMessageType::Message)
{
@ -166,13 +157,13 @@ namespace ix
{
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")
{
invokeEventCallback(ix::CobraEventType::HandshakeError,
msg->str);
invokeEventCallback(ix::CobraEventType::HandshakeError, msg->str);
}
else if (action == "auth/authenticate/ok")
{
@ -182,8 +173,7 @@ namespace ix
}
else if (action == "auth/authenticate/error")
{
invokeEventCallback(ix::CobraEventType::AuthenticationError,
msg->str);
invokeEventCallback(ix::CobraEventType::AuthenticationError, msg->str);
}
else if (action == "rtm/subscription/data")
{
@ -198,8 +188,7 @@ namespace ix
}
else if (action == "rtm/subscribe/error")
{
invokeEventCallback(ix::CobraEventType::SubscriptionError,
msg->str);
invokeEventCallback(ix::CobraEventType::SubscriptionError, msg->str);
}
else if (action == "rtm/unsubscribe/ok")
{
@ -254,7 +243,8 @@ namespace ix
return _publishMode;
}
void CobraConnection::configure(const std::string& appkey,
void CobraConnection::configure(
const std::string& appkey,
const std::string& endpoint,
const std::string& rolename,
const std::string& rolesecret,
@ -402,7 +392,8 @@ namespace ix
if (!subscriptionId.isString()) return false;
invokeEventCallback(ix::CobraEventType::Subscribed,
std::string(), WebSocketHttpHeaders(),
std::string(),
WebSocketHttpHeaders(),
subscriptionId.asString());
return true;
}
@ -420,7 +411,8 @@ namespace ix
if (!subscriptionId.isString()) return false;
invokeEventCallback(ix::CobraEventType::UnSubscribed,
std::string(), WebSocketHttpHeaders(),
std::string(),
WebSocketHttpHeaders(),
subscriptionId.asString());
return true;
}
@ -468,8 +460,10 @@ namespace ix
uint64_t msgId = id.asUInt64();
invokeEventCallback(ix::CobraEventType::Published,
std::string(), WebSocketHttpHeaders(),
std::string(), msgId);
std::string(),
WebSocketHttpHeaders(),
std::string(),
msgId);
invokePublishTrackerCallback(false, true);
@ -499,9 +493,7 @@ namespace ix
}
std::pair<CobraConnection::MsgId, std::string> CobraConnection::prePublish(
const Json::Value& channels,
const Json::Value& msg,
bool addToQueue)
const Json::Value& channels, const Json::Value& msg, bool addToQueue)
{
std::lock_guard<std::mutex> lock(_prePublishMutex);
@ -667,8 +659,7 @@ namespace ix
bool CobraConnection::publishMessage(const std::string& serializedJson)
{
auto webSocketSendInfo = _webSocket->send(serializedJson);
CobraConnection::invokeTrafficTrackerCallback(webSocketSendInfo.wireSize,
false);
CobraConnection::invokeTrafficTrackerCallback(webSocketSendInfo.wireSize, false);
return webSocketSendInfo.success;
}

View File

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

View File

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

View File

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

View File

@ -40,8 +40,7 @@ namespace ix
/// Configuration / set keys, etc...
/// All input data but the channel name is encrypted with rc4
void configure(const CobraConfig& config,
const std::string& channel);
void configure(const CobraConfig& config, const std::string& channel);
/// Setter for the list of blacklisted metrics ids.
/// That list is sorted internally for fast lookups
@ -68,10 +67,14 @@ namespace ix
/// shouldPush method for places where we want to be as lightweight as possible when
/// collecting metrics. When set to false, it is used so that we don't do double work when
/// computing whether a metrics should be sent or not.
CobraConnection::MsgId push(const std::string& id, const Json::Value& data, bool shouldPushTest = true);
CobraConnection::MsgId push(const std::string& id,
const Json::Value& data,
bool shouldPushTest = true);
/// Interface used by lua. msg is a json encoded string.
CobraConnection::MsgId push(const std::string& id, const std::string& data, bool shouldPushTest = true);
CobraConnection::MsgId push(const std::string& id,
const std::string& data,
bool shouldPushTest = true);
/// Tells whether a metric can be pushed.
/// A metric can be pushed if it satisfies those conditions:
@ -89,10 +92,16 @@ namespace ix
void setGenericAttributes(const std::string& attrName, const Json::Value& value);
/// Set a unique id for the session. A uuid can be used.
void setSession(const std::string& session) { _session = session; }
void setSession(const std::string& session)
{
_session = session;
}
/// Get the unique id used to identify the current session
const std::string& getSession() const { return _session; }
const std::string& getSession() const
{
return _session;
}
/// Return the number of milliseconds since the epoch (~1970)
uint64_t getMillisecondsSinceEpoch() const;

View File

@ -5,25 +5,24 @@
*/
#include "IXCobraMetricsThreadedPublisher.h"
#include <ixwebsocket/IXSetThreadName.h>
#include <ixwebsocket/IXSocketTLSOptions.h>
#include <ixcore/utils/IXCoreLogger.h>
#include <algorithm>
#include <stdexcept>
#include <cmath>
#include <cassert>
#include <cmath>
#include <iostream>
#include <ixcore/utils/IXCoreLogger.h>
#include <ixwebsocket/IXSetThreadName.h>
#include <ixwebsocket/IXSocketTLSOptions.h>
#include <sstream>
#include <stdexcept>
namespace ix
{
CobraMetricsThreadedPublisher::CobraMetricsThreadedPublisher() :
_stop(false)
{
_cobra_connection.setEventCallback([](const CobraEventPtr& event)
CobraMetricsThreadedPublisher::CobraMetricsThreadedPublisher()
: _stop(false)
{
_cobra_connection.setEventCallback([](const CobraEventPtr& event) {
std::stringstream ss;
if (event->type == ix::CobraEventType::Open)
@ -105,7 +104,6 @@ namespace ix
_channel = channel;
_cobra_connection.configure(config);
}
void CobraMetricsThreadedPublisher::pushMessage(MessageKind messageKind)
@ -163,13 +161,15 @@ namespace ix
{
_cobra_connection.suspend();
continue;
}; break;
};
break;
case MessageKind::Resume:
{
_cobra_connection.resume();
continue;
}; break;
};
break;
case MessageKind::Message:
{
@ -177,7 +177,8 @@ namespace ix
{
_cobra_connection.publishNext();
}
}; break;
};
break;
}
}
}

View File

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

View File

@ -11,4 +11,4 @@ void IXCoreLogger::Log(const char* msg)
_currentLogger(msg);
}
} // ix
} // namespace ix

View File

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

View File

@ -29,8 +29,7 @@
namespace ix
{
static const std::string base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
static const std::string base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
@ -82,7 +81,6 @@ namespace ix
while ((i++ < 3))
ret += '=';
}
return ret;
@ -105,7 +103,8 @@ namespace ix
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_];
in_++;
if (i == 4)
{
for (i = 0; i < 4; i++)
@ -134,9 +133,10 @@ namespace ix
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for(j = 0; (j < i - 1); j++) ret += char_array_3[j];
for (j = 0; (j < i - 1); j++)
ret += char_array_3[j];
}
return ret;
}
}
} // namespace ix

View File

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

View File

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

View File

@ -16,10 +16,10 @@
#include "IXUuid.h"
#include <sstream>
#include <string>
#include <iomanip>
#include <random>
#include <sstream>
#include <string>
namespace ix
@ -72,4 +72,4 @@ namespace ix
Uuid id;
return id.toString();
}
}
} // namespace ix

View File

@ -7,12 +7,12 @@
#include "IXSentryClient.h"
#include <chrono>
#include <iostream>
#include <fstream>
#include <sstream>
#include <iostream>
#include <ixcore/utils/IXCoreLogger.h>
#include <ixwebsocket/IXWebSocketHttpHeaders.h>
#include <ixwebsocket/IXWebSocketVersion.h>
#include <ixcore/utils/IXCoreLogger.h>
#include <sstream>
namespace ix
@ -246,19 +246,16 @@ namespace ix
std::string SentryClient::computeUrl(const std::string& project, const std::string& key)
{
std::stringstream ss;
ss << "https://sentry.io/api/"
<< project
<< "/minidump?sentry_key="
<< key;
ss << "https://sentry.io/api/" << project << "/minidump?sentry_key=" << key;
return ss.str();
}
//
// curl -v -X POST -F upload_file_minidump=@ws/crash.dmp 'https://sentry.io/api/123456/minidump?sentry_key=12344567890'
// curl -v -X POST -F upload_file_minidump=@ws/crash.dmp
// 'https://sentry.io/api/123456/minidump?sentry_key=12344567890'
//
void SentryClient::uploadMinidump(
const std::string& sentryMetadata,
void SentryClient::uploadMinidump(const std::string& sentryMetadata,
const std::string& minidumpBytes,
const std::string& project,
const std::string& key,
@ -283,13 +280,13 @@ namespace ix
httpParameters["sentry"] = sentryMetadata;
args->url = computeUrl(project, key);
args->body = _httpClient->serializeHttpFormDataParameters(multipartBoundary, httpFormDataParameters, httpParameters);
args->body = _httpClient->serializeHttpFormDataParameters(
multipartBoundary, httpFormDataParameters, httpParameters);
_httpClient->performRequest(args, onResponseCallback);
}
void SentryClient::uploadPayload(
const Json::Value& payload,
void SentryClient::uploadPayload(const Json::Value& payload,
bool verbose,
const OnResponseCallback& onResponseCallback)
{

View File

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

View File

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

View File

@ -28,10 +28,7 @@ namespace ix
return false;
}
CancellationRequest cancellationRequest = []() -> bool
{
return false;
};
CancellationRequest cancellationRequest = []() -> bool { return false; };
std::string errMsg;
return _socket->connect(hostname, port, errMsg, cancellationRequest);
@ -252,8 +249,7 @@ namespace ix
return true;
}
std::string RedisClient::prepareXaddCommand(
const std::string& stream,
std::string RedisClient::prepareXaddCommand(const std::string& stream,
const std::string& message)
{
std::stringstream ss;
@ -328,7 +324,9 @@ namespace ix
return streamId;
}
bool RedisClient::sendCommand(const std::string& commands, int commandsCount, std::string& errMsg)
bool RedisClient::sendCommand(const std::string& commands,
int commandsCount,
std::string& errMsg)
{
bool sent = _socket->writeBytes(commands, nullptr);
if (!sent)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

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

@ -16,18 +16,15 @@ FMT_BEGIN_NAMESPACE
namespace internal {
template<typename Char>
typename buffer_context<Char>::iterator vformat_to(
const std::locale& loc, buffer<Char>& buf,
basic_string_view<Char> format_str,
basic_format_args<buffer_context<Char>> args) {
const std::locale &loc, buffer<Char> &buf, basic_string_view<Char> format_str, basic_format_args<buffer_context<Char>> args)
{
using range = buffer_range<Char>;
return vformat_to<arg_formatter<range>>(buf, to_string_view(format_str), args,
internal::locale_ref(loc));
return vformat_to<arg_formatter<range>>(buf, to_string_view(format_str), args, internal::locale_ref(loc));
}
template<typename Char>
std::basic_string<Char> vformat(const std::locale& loc,
basic_string_view<Char> format_str,
basic_format_args<buffer_context<Char>> args) {
std::basic_string<Char> vformat(const std::locale &loc, basic_string_view<Char> format_str, basic_format_args<buffer_context<Char>> args)
{
basic_memory_buffer<Char> buffer;
internal::vformat_to(loc, buffer, format_str, args);
return fmt::to_string(buffer);
@ -35,41 +32,33 @@ std::basic_string<Char> vformat(const std::locale& loc,
} // namespace internal
template<typename S, typename Char = char_t<S>>
inline std::basic_string<Char> vformat(
const std::locale& loc, const S& format_str,
basic_format_args<buffer_context<Char>> args) {
inline std::basic_string<Char> vformat(const std::locale &loc, const S &format_str, basic_format_args<buffer_context<Char>> args)
{
return internal::vformat(loc, to_string_view(format_str), args);
}
template<typename S, typename... Args, typename Char = char_t<S>>
inline std::basic_string<Char> format(const std::locale& loc,
const S& format_str, Args&&... args) {
return internal::vformat(
loc, to_string_view(format_str),
{internal::make_args_checked<Args...>(format_str, args...)});
inline std::basic_string<Char> format(const std::locale &loc, const S &format_str, Args &&... args)
{
return internal::vformat(loc, to_string_view(format_str), {internal::make_args_checked<Args...>(format_str, args...)});
}
template<typename S, typename OutputIt, typename... Args,
typename Char = enable_if_t<
internal::is_output_iterator<OutputIt>::value, char_t<S>>>
inline OutputIt vformat_to(OutputIt out, const std::locale& loc,
const S& format_str,
format_args_t<OutputIt, Char> args) {
typename Char = enable_if_t<internal::is_output_iterator<OutputIt>::value, char_t<S>>>
inline OutputIt vformat_to(OutputIt out, const std::locale &loc, const S &format_str, format_args_t<OutputIt, Char> args)
{
using range = internal::output_range<OutputIt, Char>;
return vformat_to<arg_formatter<range>>(
range(out), to_string_view(format_str), args, internal::locale_ref(loc));
return vformat_to<arg_formatter<range>>(range(out), to_string_view(format_str), args, internal::locale_ref(loc));
}
template<typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(internal::is_output_iterator<OutputIt>::value&&
internal::is_string<S>::value)>
inline OutputIt format_to(OutputIt out, const std::locale& loc,
const S& format_str, Args&&... args) {
FMT_ENABLE_IF(internal::is_output_iterator<OutputIt>::value &&internal::is_string<S>::value)>
inline OutputIt format_to(OutputIt out, const std::locale &loc, const S &format_str, Args &&... args)
{
internal::check_format_string<Args...>(format_str);
using context = format_context_t<OutputIt, char_t<S>>;
format_arg_store<context, Args...> as{args...};
return vformat_to(out, loc, to_string_view(format_str),
basic_format_args<context>(as));
return vformat_to(out, loc, to_string_view(format_str), basic_format_args<context>(as));
}
FMT_END_NAMESPACE

View File

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

View File

@ -30,8 +30,7 @@
#if FMT_HAS_INCLUDE("winapifamily.h")
#include <winapifamily.h>
#endif
#if FMT_HAS_INCLUDE("fcntl.h") && \
(!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
#if FMT_HAS_INCLUDE("fcntl.h") && (!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
#include <fcntl.h> // for O_RDONLY
#define FMT_USE_FCNTL 1
#else
@ -64,7 +63,8 @@
// equals to EINTR.
#ifndef _WIN32
#define FMT_RETRY_VAL(result, expression, error_result) \
do { \
do \
{ \
(result) = (expression); \
} while ((result) == (error_result) && errno == EINTR)
#else
@ -100,47 +100,63 @@ FMT_BEGIN_NAMESPACE
format(std::string("{}"), 42);
\endrst
*/
template <typename Char> class basic_cstring_view {
template<typename Char>
class basic_cstring_view
{
private:
const Char *data_;
public:
/** Constructs a string reference object from a C string. */
basic_cstring_view(const Char* s) : data_(s) {}
basic_cstring_view(const Char *s)
: data_(s)
{}
/**
\rst
Constructs a string reference from an ``std::string`` object.
\endrst
*/
basic_cstring_view(const std::basic_string<Char>& s) : data_(s.c_str()) {}
basic_cstring_view(const std::basic_string<Char> &s)
: data_(s.c_str())
{}
/** Returns the pointer to a C string. */
const Char* c_str() const { return data_; }
const Char *c_str() const
{
return data_;
}
};
using cstring_view = basic_cstring_view<char>;
using wcstring_view = basic_cstring_view<wchar_t>;
// An error code.
class error_code {
class error_code
{
private:
int value_;
public:
explicit error_code(int value = 0) FMT_NOEXCEPT : value_(value) {}
int get() const FMT_NOEXCEPT { return value_; }
int get() const FMT_NOEXCEPT
{
return value_;
}
};
// A buffered file.
class buffered_file {
class buffered_file
{
private:
FILE *file_;
friend class file;
explicit buffered_file(FILE* f) : file_(f) {}
explicit buffered_file(FILE *f)
: file_(f)
{}
public:
buffered_file(const buffered_file &) = delete;
@ -153,11 +169,13 @@ class buffered_file {
FMT_API ~buffered_file() FMT_NOEXCEPT;
public:
buffered_file(buffered_file&& other) FMT_NOEXCEPT : file_(other.file_) {
buffered_file(buffered_file &&other) FMT_NOEXCEPT : file_(other.file_)
{
other.file_ = nullptr;
}
buffered_file& operator=(buffered_file&& other) {
buffered_file &operator=(buffered_file &&other)
{
close();
file_ = other.file_;
other.file_ = nullptr;
@ -171,18 +189,23 @@ class buffered_file {
FMT_API void close();
// 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
// of MinGW that define fileno as a macro.
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);
}
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...));
}
};
@ -194,16 +217,20 @@ class buffered_file {
// closing the file multiple times will cause a crash on Windows rather
// than an exception. You can get standard behavior by overriding the
// invalid parameter handler with _set_invalid_parameter_handler.
class file {
class file
{
private:
int fd_; // File descriptor.
// Constructs a file object with a given descriptor.
explicit file(int fd) : fd_(fd) {}
explicit file(int fd)
: fd_(fd)
{}
public:
// Possible values for the oflag argument to the constructor.
enum {
enum
{
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing.
@ -219,9 +246,13 @@ class file {
file(const file &) = delete;
void operator=(const file &) = delete;
file(file&& other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; }
file(file &&other) FMT_NOEXCEPT : fd_(other.fd_)
{
other.fd_ = -1;
}
file& operator=(file&& other) FMT_NOEXCEPT {
file &operator=(file &&other) FMT_NOEXCEPT
{
close();
fd_ = other.fd_;
other.fd_ = -1;
@ -232,7 +263,10 @@ class file {
FMT_API ~file() FMT_NOEXCEPT;
// Returns the file descriptor.
int descriptor() const FMT_NOEXCEPT { return fd_; }
int descriptor() const FMT_NOEXCEPT
{
return fd_;
}
// Closes the file.
FMT_API void close();
@ -274,20 +308,29 @@ long getpagesize();
#ifdef FMT_LOCALE
// A "C" numeric locale.
class Locale {
class Locale
{
private:
#ifdef _WIN32
using locale_t = _locale_t;
enum { LC_NUMERIC_MASK = LC_NUMERIC };
enum
{
LC_NUMERIC_MASK = LC_NUMERIC
};
static locale_t newlocale(int category_mask, const char* locale, locale_t) {
static locale_t newlocale(int category_mask, const char *locale, locale_t)
{
return _create_locale(category_mask, locale);
}
static void freelocale(locale_t locale) { _free_locale(locale); }
static void freelocale(locale_t locale)
{
_free_locale(locale);
}
static double strtod_l(const char* nptr, char** endptr, _locale_t locale) {
static double strtod_l(const char *nptr, char **endptr, _locale_t locale)
{
return _strtod_l(nptr, endptr, locale);
}
#endif
@ -299,16 +342,26 @@ class Locale {
Locale(const Locale &) = delete;
void operator=(const Locale &) = delete;
Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", nullptr)) {
if (!locale_) FMT_THROW(system_error(errno, "cannot create locale"));
Locale()
: 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
// of the parsed input.
double strtod(const char*& str) const {
double strtod(const char *&str) const
{
char *end = nullptr;
double result = strtod_l(str, &end, locale_);
str = end;

View File

@ -18,57 +18,84 @@ namespace internal {
// Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers.
template <bool IsSigned> struct int_checker {
template <typename T> static bool fits_in_int(T value) {
template<bool IsSigned>
struct int_checker
{
template<typename T>
static bool fits_in_int(T value)
{
unsigned max = max_value<int>();
return value <= max;
}
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(bool)
{
return true;
}
static bool fits_in_int(int) { return true; }
};
class printf_precision_handler {
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
{
public:
template<typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
int operator()(T value) {
int operator()(T value)
{
if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
FMT_THROW(format_error("number is too big"));
return (std::max)(static_cast<int>(value), 0);
}
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"));
return 0;
}
};
// 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)>
bool operator()(T value) {
bool operator()(T value)
{
return value == 0;
}
template<typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
bool operator()(T) {
bool operator()(T)
{
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>
class arg_converter
{
private:
using char_type = typename Context::char_type;
@ -77,41 +104,53 @@ template <typename T, typename Context> class arg_converter {
public:
arg_converter(basic_format_arg<Context> &arg, char_type type)
: arg_(arg), type_(type) {}
: arg_(arg)
, type_(type)
{}
void operator()(bool value) {
if (type_ != 's') operator()<bool>(value);
void operator()(bool value)
{
if (type_ != 's')
operator()<bool>(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';
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.
if (is_signed) {
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)));
if (is_signed)
{
arg_ = internal::make_arg<Context>(static_cast<int>(static_cast<target_type>(value)));
}
} else {
if (is_signed) {
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
{
if (is_signed)
{
// glibc's printf doesn't sign extend arguments of smaller types:
// std::printf("%lld", -42); // prints "4294967254"
// but we don't have to do the same because it's a UB.
arg_ = internal::make_arg<Context>(static_cast<long long>(value));
} else {
arg_ = internal::make_arg<Context>(
static_cast<typename make_unsigned_or_bool<U>::type>(value));
}
else
{
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)>
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.
@ -119,77 +158,93 @@ template <typename T, typename Context> class arg_converter {
// type depending on the type specifier: 'd' and 'i' - signed, other -
// unsigned).
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);
}
// Converts an integer argument to char for printf.
template <typename Context> class char_converter {
template<typename Context>
class char_converter
{
private:
basic_format_arg<Context> &arg_;
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)>
void operator()(T value) {
arg_ = internal::make_arg<Context>(
static_cast<typename Context::char_type>(value));
void operator()(T value)
{
arg_ = internal::make_arg<Context>(static_cast<typename Context::char_type>(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
// left alignment if it is negative.
template <typename Char> class printf_width_handler {
template<typename Char>
class printf_width_handler
{
private:
using format_specs = basic_format_specs<Char>;
format_specs &specs_;
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)>
unsigned operator()(T value) {
unsigned operator()(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;
width = 0 - width;
}
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);
}
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"));
return 0;
}
};
template<typename Char, typename Context>
void printf(buffer<Char>& buf, basic_string_view<Char> format,
basic_format_args<Context> args) {
void printf(buffer<Char> &buf, basic_string_view<Char> format, basic_format_args<Context> args)
{
Context(std::back_inserter(buf), format, args).format();
}
template<typename OutputIt, typename Char, typename Context>
internal::truncating_iterator<OutputIt> printf(
internal::truncating_iterator<OutputIt> it, basic_string_view<Char> format,
basic_format_args<Context> args) {
internal::truncating_iterator<OutputIt> it, basic_string_view<Char> format, basic_format_args<Context> args)
{
return Context(it, format, args).format();
}
} // namespace internal
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
@ -197,7 +252,8 @@ template <typename OutputIt, typename Char> class basic_printf_context;
\endrst
*/
template<typename Range>
class printf_arg_formatter : public internal::arg_formatter_base<Range> {
class printf_arg_formatter : public internal::arg_formatter_base<Range>
{
public:
using iterator = typename Range::iterator;
@ -208,12 +264,14 @@ class printf_arg_formatter : public internal::arg_formatter_base<Range> {
context_type &context_;
void write_null_pointer(char) {
void write_null_pointer(char)
{
this->specs()->type = 0;
this->write("(nil)");
}
void write_null_pointer(wchar_t) {
void write_null_pointer(wchar_t)
{
this->specs()->type = 0;
this->write(L"(nil)");
}
@ -229,18 +287,25 @@ class printf_arg_formatter : public internal::arg_formatter_base<Range> {
\endrst
*/
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)>
iterator operator()(T value) {
iterator operator()(T value)
{
// MSVC2013 fails to compile separate overloads for bool and char_type so
// 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);
if (fmt_specs.type != 's')
return base::operator()(value ? 1 : 0);
fmt_specs.type = 0;
this->write(value != 0);
} else if (std::is_same<T, char_type>::value) {
}
else if (std::is_same<T, char_type>::value)
{
format_specs &fmt_specs = *this->specs();
if (fmt_specs.type && fmt_specs.type != 'c')
return (*this)(static_cast<int>(value));
@ -248,19 +313,23 @@ class printf_arg_formatter : public internal::arg_formatter_base<Range> {
fmt_specs.alt = false;
fmt_specs.align = align::right;
return base::operator()(value);
} else {
}
else
{
return base::operator()(value);
}
return this->out();
}
template<typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
iterator operator()(T value) {
iterator operator()(T value)
{
return base::operator()(value);
}
/** Formats a null-terminated C string. */
iterator operator()(const char* value) {
iterator operator()(const char *value)
{
if (value)
base::operator()(value);
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. */
iterator operator()(const wchar_t* value) {
iterator operator()(const wchar_t *value)
{
if (value)
base::operator()(value);
else if (this->specs()->type == 'p')
@ -281,47 +351,61 @@ class printf_arg_formatter : public internal::arg_formatter_base<Range> {
return this->out();
}
iterator operator()(basic_string_view<char_type> value) {
iterator operator()(basic_string_view<char_type> value)
{
return base::operator()(value);
}
iterator operator()(monostate value) { return base::operator()(value); }
iterator operator()(monostate value)
{
return base::operator()(value);
}
/** Formats a pointer. */
iterator operator()(const void* value) {
if (value) return base::operator()(value);
iterator operator()(const void *value)
{
if (value)
return base::operator()(value);
this->specs()->type = 0;
write_null_pointer(char_type());
return this->out();
}
/** 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_);
return this->out();
}
};
template <typename T> struct printf_formatter {
template<typename T>
struct printf_formatter
{
template<typename ParseContext>
auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
auto parse(ParseContext &ctx) -> decltype(ctx.begin())
{
return ctx.begin();
}
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);
return ctx.out();
}
};
/** 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>
class basic_printf_context
{
public:
/** The character type for the output. */
using char_type = Char;
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:
using format_specs = basic_format_specs<char_type>;
@ -330,8 +414,7 @@ template <typename OutputIt, typename Char> class basic_printf_context {
basic_format_args<basic_printf_context> args_;
basic_format_parse_context<Char> parse_ctx_;
static void parse_flags(format_specs& specs, const Char*& it,
const Char* end);
static void parse_flags(format_specs &specs, const Char *&it, const Char *end);
// Returns the argument with specified index or, if arg_index is -1, the next
// argument.
@ -348,18 +431,33 @@ template <typename OutputIt, typename Char> class basic_printf_context {
appropriate lifetimes.
\endrst
*/
basic_printf_context(OutputIt out, basic_string_view<char_type> format_str,
basic_format_args<basic_printf_context> args)
: out_(out), args_(args), parse_ctx_(format_str) {}
basic_printf_context(OutputIt out, basic_string_view<char_type> format_str, basic_format_args<basic_printf_context> args)
: out_(out)
, args_(args)
, parse_ctx_(format_str)
{}
OutputIt out() { return out_; }
void advance_to(OutputIt it) { out_ = it; }
OutputIt out()
{
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);
}
@ -369,11 +467,12 @@ template <typename OutputIt, typename Char> class basic_printf_context {
};
template<typename OutputIt, typename Char>
void basic_printf_context<OutputIt, Char>::parse_flags(format_specs& specs,
const Char*& it,
const Char* end) {
for (; it != end; ++it) {
switch (*it) {
void basic_printf_context<OutputIt, Char>::parse_flags(format_specs &specs, const Char *&it, const Char *end)
{
for (; it != end; ++it)
{
switch (*it)
{
case '-':
specs.align = align::left;
break;
@ -396,8 +495,8 @@ void basic_printf_context<OutputIt, Char>::parse_flags(format_specs& specs,
}
template<typename OutputIt, typename Char>
typename basic_printf_context<OutputIt, Char>::format_arg
basic_printf_context<OutputIt, Char>::get_arg(int arg_index) {
typename basic_printf_context<OutputIt, Char>::format_arg basic_printf_context<OutputIt, Char>::get_arg(int arg_index)
{
if (arg_index < 0)
arg_index = parse_ctx_.next_arg_id();
else
@ -406,21 +505,27 @@ basic_printf_context<OutputIt, Char>::get_arg(int arg_index) {
}
template<typename OutputIt, typename Char>
int basic_printf_context<OutputIt, Char>::parse_header(
const Char*& it, const Char* end, format_specs& specs) {
int basic_printf_context<OutputIt, Char>::parse_header(const Char *&it, const Char *end, format_specs &specs)
{
int arg_index = -1;
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
// preceded with '0' flag(s).
internal::error_handler 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;
arg_index = value;
} else {
if (c == '0') specs.fill[0] = '0';
if (value != 0) {
}
else
{
if (c == '0')
specs.fill[0] = '0';
if (value != 0)
{
// Nonzero value means that we parsed width and don't need to
// parse it or flags again, so return now.
specs.width = value;
@ -430,14 +535,17 @@ int basic_printf_context<OutputIt, Char>::parse_header(
}
parse_flags(specs, it, end);
// Parse width.
if (it != end) {
if (*it >= '0' && *it <= '9') {
if (it != end)
{
if (*it >= '0' && *it <= '9')
{
internal::error_handler eh;
specs.width = parse_nonnegative_int(it, end, eh);
} else if (*it == '*') {
}
else if (*it == '*')
{
++it;
specs.width = static_cast<int>(visit_format_arg(
internal::printf_width_handler<char_type>(specs), get_arg()));
specs.width = static_cast<int>(visit_format_arg(internal::printf_width_handler<char_type>(specs), get_arg()));
}
}
return arg_index;
@ -445,15 +553,19 @@ int basic_printf_context<OutputIt, Char>::parse_header(
template<typename OutputIt, typename Char>
template<typename ArgFormatter>
OutputIt basic_printf_context<OutputIt, Char>::format() {
OutputIt basic_printf_context<OutputIt, Char>::format()
{
auto out = this->out();
const Char *start = parse_ctx_.begin();
const Char *end = parse_ctx_.end();
auto it = start;
while (it != end) {
while (it != end)
{
char_type c = *it++;
if (c != '%') continue;
if (it != end && *it == c) {
if (c != '%')
continue;
if (it != end && *it == c)
{
out = std::copy(start, it, out);
start = ++it;
continue;
@ -465,20 +577,26 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
// Parse argument index, flags and width.
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.
if (it != end && *it == '.') {
if (it != end && *it == '.')
{
++it;
c = it != end ? *it : 0;
if ('0' <= c && c <= '9') {
if ('0' <= c && c <= '9')
{
internal::error_handler eh;
specs.precision = parse_nonnegative_int(it, end, eh);
} else if (c == '*') {
}
else if (c == '*')
{
++it;
specs.precision =
static_cast<int>(visit_format_arg(internal::printf_precision_handler(), get_arg()));
} else {
specs.precision = static_cast<int>(visit_format_arg(internal::printf_precision_handler(), get_arg()));
}
else
{
specs.precision = 0;
}
}
@ -486,7 +604,8 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
format_arg arg = get_arg(arg_index);
if (specs.alt && visit_format_arg(internal::is_zero_int(), arg))
specs.alt = false;
if (specs.fill[0] == '0') {
if (specs.fill[0] == '0')
{
if (arg.is_arithmetic())
specs.align = align::numeric;
else
@ -497,22 +616,29 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
c = it != end ? *it++ : 0;
char_type t = it != end ? *it : 0;
using internal::convert_arg;
switch (c) {
switch (c)
{
case 'h':
if (t == 'h') {
if (t == 'h')
{
++it;
t = it != end ? *it : 0;
convert_arg<signed char>(arg, t);
} else {
}
else
{
convert_arg<short>(arg, t);
}
break;
case 'l':
if (t == 'l') {
if (t == 'l')
{
++it;
t = it != end ? *it : 0;
convert_arg<long long>(arg, t);
} else {
}
else
{
convert_arg<long>(arg, t);
}
break;
@ -535,18 +661,20 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
}
// 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++);
if (arg.is_integral()) {
if (arg.is_integral())
{
// Normalize type.
switch (specs.type) {
switch (specs.type)
{
case 'i':
case 'u':
specs.type = 'd';
break;
case 'c':
visit_format_arg(internal::char_converter<basic_printf_context>(arg),
arg);
visit_format_arg(internal::char_converter<basic_printf_context>(arg), arg);
break;
}
}
@ -560,9 +688,7 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
}
template<typename Char>
using basic_printf_context_t =
basic_printf_context<std::back_insert_iterator<internal::buffer<Char>>,
Char>;
using basic_printf_context_t = basic_printf_context<std::back_insert_iterator<internal::buffer<Char>>, Char>;
using printf_context = basic_printf_context_t<char>;
using wprintf_context = basic_printf_context_t<wchar_t>;
@ -577,8 +703,8 @@ using wprintf_args = basic_format_args<wprintf_context>;
\endrst
*/
template<typename... Args>
inline format_arg_store<printf_context, Args...> make_printf_args(
const Args&... args) {
inline format_arg_store<printf_context, Args...> make_printf_args(const Args &... args)
{
return {args...};
}
@ -589,14 +715,14 @@ inline format_arg_store<printf_context, Args...> make_printf_args(
\endrst
*/
template<typename... Args>
inline format_arg_store<wprintf_context, Args...> make_wprintf_args(
const Args&... args) {
inline format_arg_store<wprintf_context, Args...> make_wprintf_args(const Args &... args)
{
return {args...};
}
template<typename S, typename Char = char_t<S>>
inline std::basic_string<Char> vsprintf(
const S& format, basic_format_args<basic_printf_context_t<Char>> args) {
inline std::basic_string<Char> vsprintf(const S &format, basic_format_args<basic_printf_context_t<Char>> args)
{
basic_memory_buffer<Char> buffer;
printf(buffer, to_string_view(format), args);
return to_string(buffer);
@ -611,22 +737,20 @@ inline std::basic_string<Char> vsprintf(
std::string message = fmt::sprintf("The answer is %d", 42);
\endrst
*/
template <typename S, typename... Args,
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) {
template<typename S, typename... Args, 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)
{
using context = basic_printf_context_t<Char>;
return vsprintf(to_string_view(format), {make_format_args<context>(args...)});
}
template<typename S, typename Char = char_t<S>>
inline int vfprintf(std::FILE* f, const S& format,
basic_format_args<basic_printf_context_t<Char>> args) {
inline int vfprintf(std::FILE *f, const S &format, basic_format_args<basic_printf_context_t<Char>> args)
{
basic_memory_buffer<Char> buffer;
printf(buffer, to_string_view(format), args);
std::size_t size = buffer.size();
return std::fwrite(buffer.data(), sizeof(Char), size, f) < size
? -1
: static_cast<int>(size);
return std::fwrite(buffer.data(), sizeof(Char), size, f) < 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");
\endrst
*/
template <typename S, typename... Args,
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) {
template<typename S, typename... Args, 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)
{
using context = basic_printf_context_t<Char>;
return vfprintf(f, to_string_view(format),
{make_format_args<context>(args...)});
return vfprintf(f, to_string_view(format), {make_format_args<context>(args...)});
}
template<typename S, typename Char = char_t<S>>
inline int vprintf(const S& format,
basic_format_args<basic_printf_context_t<Char>> args) {
inline int vprintf(const S &format, basic_format_args<basic_printf_context_t<Char>> 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);
\endrst
*/
template <typename S, typename... Args,
FMT_ENABLE_IF(internal::is_string<S>::value)>
inline int printf(const S& format_str, const Args&... args) {
template<typename S, typename... Args, FMT_ENABLE_IF(internal::is_string<S>::value)>
inline int printf(const S &format_str, const Args &... args)
{
using context = basic_printf_context_t<char_t<S>>;
return vprintf(to_string_view(format_str),
{make_format_args<context>(args...)});
return vprintf(to_string_view(format_str), {make_format_args<context>(args...)});
}
template<typename S, typename Char = char_t<S>>
inline int vfprintf(std::basic_ostream<Char>& os, const S& format,
basic_format_args<basic_printf_context_t<Char>> args) {
inline int vfprintf(std::basic_ostream<Char> &os, const S &format, basic_format_args<basic_printf_context_t<Char>> args)
{
basic_memory_buffer<Char> buffer;
printf(buffer, to_string_view(format), args);
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. */
template <typename ArgFormatter, typename Char,
typename Context =
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) {
template<typename ArgFormatter, typename Char, typename Context = 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);
Context(iter, format_str, args).template format<ArgFormatter>();
return iter;
@ -700,11 +819,10 @@ typename ArgFormatter::iterator vprintf(internal::buffer<Char>& out,
\endrst
*/
template<typename S, typename... Args, typename Char = char_t<S>>
inline int fprintf(std::basic_ostream<Char>& os, const S& format_str,
const Args&... args) {
inline int fprintf(std::basic_ostream<Char> &os, const S &format_str, const Args &... args)
{
using context = basic_printf_context_t<Char>;
return vfprintf(os, to_string_view(format_str),
{make_format_args<context>(args...)});
return vfprintf(os, to_string_view(format_str), {make_format_args<context>(args...)});
}
FMT_END_NAMESPACE

View File

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