mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-01-12 04:42:36 -03:00
Merge #17682: util: Update tinyformat to upstream
978b25528c
util: Update tinyformat to upstream (Wladimir J. van der Laan) Pull request description: Last update was in 2017. Updates tinyformat to upstream commit c42f/tinyformat@705e3f4e1d. Re-apply (and mark) bitcoin core specific changes. No changes that affect our use, as far as I can see, but this gets rid of the gcc `-Wimplicit-fallthrough` warnings, at least. ACKs for top commit: MarcoFalke: ACK978b25528c
, extracted our patches based on the last update, did the update to v2.3.0 myself and re-applied the patches. Only diff is NULL/nullptr and explicit 🔝 Tree-SHA512: 2ba09e1095878d088520f379d545b40c7286ef199ecbbc17fdd5c85bca447d9b0c7a1829d4038bb6d432cd1ff92ad7bba75c0f2f96c71aeb6fa6031002f1ea1d
This commit is contained in:
commit
5bf1909dd9
1 changed files with 280 additions and 183 deletions
463
src/tinyformat.h
463
src/tinyformat.h
|
@ -33,6 +33,7 @@
|
||||||
//
|
//
|
||||||
// * Type safety and extensibility for user defined types.
|
// * Type safety and extensibility for user defined types.
|
||||||
// * C99 printf() compatibility, to the extent possible using std::ostream
|
// * C99 printf() compatibility, to the extent possible using std::ostream
|
||||||
|
// * POSIX extension for positional arguments
|
||||||
// * Simplicity and minimalism. A single header file to include and distribute
|
// * Simplicity and minimalism. A single header file to include and distribute
|
||||||
// with your projects.
|
// with your projects.
|
||||||
// * Augment rather than replace the standard stream formatting mechanism
|
// * Augment rather than replace the standard stream formatting mechanism
|
||||||
|
@ -42,7 +43,7 @@
|
||||||
// Main interface example usage
|
// Main interface example usage
|
||||||
// ----------------------------
|
// ----------------------------
|
||||||
//
|
//
|
||||||
// To print a date to std::cout:
|
// To print a date to std::cout for American usage:
|
||||||
//
|
//
|
||||||
// std::string weekday = "Wednesday";
|
// std::string weekday = "Wednesday";
|
||||||
// const char* month = "July";
|
// const char* month = "July";
|
||||||
|
@ -52,6 +53,14 @@
|
||||||
//
|
//
|
||||||
// tfm::printf("%s, %s %d, %.2d:%.2d\n", weekday, month, day, hour, min);
|
// tfm::printf("%s, %s %d, %.2d:%.2d\n", weekday, month, day, hour, min);
|
||||||
//
|
//
|
||||||
|
// POSIX extension for positional arguments is available.
|
||||||
|
// The ability to rearrange formatting arguments is an important feature
|
||||||
|
// for localization because the word order may vary in different languages.
|
||||||
|
//
|
||||||
|
// Previous example for German usage. Arguments are reordered:
|
||||||
|
//
|
||||||
|
// tfm::printf("%1$s, %3$d. %2$s, %4$d:%5$.2d\n", weekday, month, day, hour, min);
|
||||||
|
//
|
||||||
// The strange types here emphasize the type safety of the interface; it is
|
// The strange types here emphasize the type safety of the interface; it is
|
||||||
// possible to print a std::string using the "%s" conversion, and a
|
// possible to print a std::string using the "%s" conversion, and a
|
||||||
// size_t using the "%d" conversion. A similar result could be achieved
|
// size_t using the "%d" conversion. A similar result could be achieved
|
||||||
|
@ -133,12 +142,17 @@ namespace tfm = tinyformat;
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Implementation details.
|
// Implementation details.
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cassert>
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <stdexcept>
|
#include <stdexcept> // Added for Bitcoin Core
|
||||||
|
|
||||||
|
#ifndef TINYFORMAT_ASSERT
|
||||||
|
# include <cassert>
|
||||||
|
# define TINYFORMAT_ASSERT(cond) assert(cond)
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef TINYFORMAT_ERROR
|
#ifndef TINYFORMAT_ERROR
|
||||||
|
# include <cassert>
|
||||||
# define TINYFORMAT_ERROR(reason) assert(0 && reason)
|
# define TINYFORMAT_ERROR(reason) assert(0 && reason)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -149,13 +163,13 @@ namespace tfm = tinyformat;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(__GLIBCXX__) && __GLIBCXX__ < 20080201
|
#if defined(__GLIBCXX__) && __GLIBCXX__ < 20080201
|
||||||
// std::showpos is broken on old libstdc++ as provided with OSX. See
|
// std::showpos is broken on old libstdc++ as provided with macOS. See
|
||||||
// http://gcc.gnu.org/ml/libstdc++/2007-11/msg00075.html
|
// http://gcc.gnu.org/ml/libstdc++/2007-11/msg00075.html
|
||||||
# define TINYFORMAT_OLD_LIBSTDCPLUSPLUS_WORKAROUND
|
# define TINYFORMAT_OLD_LIBSTDCPLUSPLUS_WORKAROUND
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
// Workaround OSX linker warning: Xcode uses different default symbol
|
// Workaround macOS linker warning: Xcode uses different default symbol
|
||||||
// visibilities for static libs vs executables (see issue #25)
|
// visibilities for static libs vs executables (see issue #25)
|
||||||
# define TINYFORMAT_HIDDEN __attribute__((visibility("hidden")))
|
# define TINYFORMAT_HIDDEN __attribute__((visibility("hidden")))
|
||||||
#else
|
#else
|
||||||
|
@ -164,6 +178,7 @@ namespace tfm = tinyformat;
|
||||||
|
|
||||||
namespace tinyformat {
|
namespace tinyformat {
|
||||||
|
|
||||||
|
// Added for Bitcoin Core
|
||||||
class format_error: public std::runtime_error
|
class format_error: public std::runtime_error
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -218,7 +233,7 @@ template<int n> struct is_wchar<wchar_t[n]> {};
|
||||||
template<typename T, typename fmtT, bool convertible = is_convertible<T, fmtT>::value>
|
template<typename T, typename fmtT, bool convertible = is_convertible<T, fmtT>::value>
|
||||||
struct formatValueAsType
|
struct formatValueAsType
|
||||||
{
|
{
|
||||||
static void invoke(std::ostream& /*out*/, const T& /*value*/) { assert(0); }
|
static void invoke(std::ostream& /*out*/, const T& /*value*/) { TINYFORMAT_ASSERT(0); }
|
||||||
};
|
};
|
||||||
// Specialized version for types that can actually be converted to fmtT, as
|
// Specialized version for types that can actually be converted to fmtT, as
|
||||||
// indicated by the "convertible" template parameter.
|
// indicated by the "convertible" template parameter.
|
||||||
|
@ -240,8 +255,7 @@ struct formatZeroIntegerWorkaround<T,true>
|
||||||
{
|
{
|
||||||
static bool invoke(std::ostream& out, const T& value)
|
static bool invoke(std::ostream& out, const T& value)
|
||||||
{
|
{
|
||||||
if (static_cast<int>(value) == 0 && out.flags() & std::ios::showpos)
|
if (static_cast<int>(value) == 0 && out.flags() & std::ios::showpos) {
|
||||||
{
|
|
||||||
out << "+0";
|
out << "+0";
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -282,7 +296,7 @@ inline void formatTruncated(std::ostream& out, const T& value, int ntrunc)
|
||||||
inline void formatTruncated(std::ostream& out, type* value, int ntrunc) \
|
inline void formatTruncated(std::ostream& out, type* value, int ntrunc) \
|
||||||
{ \
|
{ \
|
||||||
std::streamsize len = 0; \
|
std::streamsize len = 0; \
|
||||||
while(len < ntrunc && value[len] != 0) \
|
while (len < ntrunc && value[len] != 0) \
|
||||||
++len; \
|
++len; \
|
||||||
out.write(value, len); \
|
out.write(value, len); \
|
||||||
}
|
}
|
||||||
|
@ -328,15 +342,14 @@ inline void formatValue(std::ostream& out, const char* /*fmtBegin*/,
|
||||||
// could otherwise lead to a crash when printing a dangling (const char*).
|
// could otherwise lead to a crash when printing a dangling (const char*).
|
||||||
const bool canConvertToChar = detail::is_convertible<T,char>::value;
|
const bool canConvertToChar = detail::is_convertible<T,char>::value;
|
||||||
const bool canConvertToVoidPtr = detail::is_convertible<T, const void*>::value;
|
const bool canConvertToVoidPtr = detail::is_convertible<T, const void*>::value;
|
||||||
if(canConvertToChar && *(fmtEnd-1) == 'c')
|
if (canConvertToChar && *(fmtEnd-1) == 'c')
|
||||||
detail::formatValueAsType<T, char>::invoke(out, value);
|
detail::formatValueAsType<T, char>::invoke(out, value);
|
||||||
else if(canConvertToVoidPtr && *(fmtEnd-1) == 'p')
|
else if (canConvertToVoidPtr && *(fmtEnd-1) == 'p')
|
||||||
detail::formatValueAsType<T, const void*>::invoke(out, value);
|
detail::formatValueAsType<T, const void*>::invoke(out, value);
|
||||||
#ifdef TINYFORMAT_OLD_LIBSTDCPLUSPLUS_WORKAROUND
|
#ifdef TINYFORMAT_OLD_LIBSTDCPLUSPLUS_WORKAROUND
|
||||||
else if(detail::formatZeroIntegerWorkaround<T>::invoke(out, value)) /**/;
|
else if (detail::formatZeroIntegerWorkaround<T>::invoke(out, value)) /**/;
|
||||||
#endif
|
#endif
|
||||||
else if(ntrunc >= 0)
|
else if (ntrunc >= 0) {
|
||||||
{
|
|
||||||
// Take care not to overread C strings in truncating conversions like
|
// Take care not to overread C strings in truncating conversions like
|
||||||
// "%.4s" where at most 4 characters may be read.
|
// "%.4s" where at most 4 characters may be read.
|
||||||
detail::formatTruncated(out, value, ntrunc);
|
detail::formatTruncated(out, value, ntrunc);
|
||||||
|
@ -351,8 +364,7 @@ inline void formatValue(std::ostream& out, const char* /*fmtBegin*/,
|
||||||
inline void formatValue(std::ostream& out, const char* /*fmtBegin*/, \
|
inline void formatValue(std::ostream& out, const char* /*fmtBegin*/, \
|
||||||
const char* fmtEnd, int /**/, charType value) \
|
const char* fmtEnd, int /**/, charType value) \
|
||||||
{ \
|
{ \
|
||||||
switch(*(fmtEnd-1)) \
|
switch (*(fmtEnd-1)) { \
|
||||||
{ \
|
|
||||||
case 'u': case 'd': case 'i': case 'o': case 'X': case 'x': \
|
case 'u': case 'd': case 'i': case 'o': case 'X': case 'x': \
|
||||||
out << static_cast<int>(value); break; \
|
out << static_cast<int>(value); break; \
|
||||||
default: \
|
default: \
|
||||||
|
@ -490,19 +502,19 @@ namespace detail {
|
||||||
|
|
||||||
// Type-opaque holder for an argument to format(), with associated actions on
|
// Type-opaque holder for an argument to format(), with associated actions on
|
||||||
// the type held as explicit function pointers. This allows FormatArg's for
|
// the type held as explicit function pointers. This allows FormatArg's for
|
||||||
// each argument to be allocated as a homogenous array inside FormatList
|
// each argument to be allocated as a homogeneous array inside FormatList
|
||||||
// whereas a naive implementation based on inheritance does not.
|
// whereas a naive implementation based on inheritance does not.
|
||||||
class FormatArg
|
class FormatArg
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
FormatArg()
|
FormatArg()
|
||||||
: m_value(nullptr),
|
: m_value(NULL),
|
||||||
m_formatImpl(nullptr),
|
m_formatImpl(NULL),
|
||||||
m_toIntImpl(nullptr)
|
m_toIntImpl(NULL)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
explicit FormatArg(const T& value)
|
FormatArg(const T& value)
|
||||||
: m_value(static_cast<const void*>(&value)),
|
: m_value(static_cast<const void*>(&value)),
|
||||||
m_formatImpl(&formatImpl<T>),
|
m_formatImpl(&formatImpl<T>),
|
||||||
m_toIntImpl(&toIntImpl<T>)
|
m_toIntImpl(&toIntImpl<T>)
|
||||||
|
@ -511,15 +523,15 @@ class FormatArg
|
||||||
void format(std::ostream& out, const char* fmtBegin,
|
void format(std::ostream& out, const char* fmtBegin,
|
||||||
const char* fmtEnd, int ntrunc) const
|
const char* fmtEnd, int ntrunc) const
|
||||||
{
|
{
|
||||||
assert(m_value);
|
TINYFORMAT_ASSERT(m_value);
|
||||||
assert(m_formatImpl);
|
TINYFORMAT_ASSERT(m_formatImpl);
|
||||||
m_formatImpl(out, fmtBegin, fmtEnd, ntrunc, m_value);
|
m_formatImpl(out, fmtBegin, fmtEnd, ntrunc, m_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
int toInt() const
|
int toInt() const
|
||||||
{
|
{
|
||||||
assert(m_value);
|
TINYFORMAT_ASSERT(m_value);
|
||||||
assert(m_toIntImpl);
|
TINYFORMAT_ASSERT(m_toIntImpl);
|
||||||
return m_toIntImpl(m_value);
|
return m_toIntImpl(m_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -549,36 +561,68 @@ class FormatArg
|
||||||
inline int parseIntAndAdvance(const char*& c)
|
inline int parseIntAndAdvance(const char*& c)
|
||||||
{
|
{
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for(;*c >= '0' && *c <= '9'; ++c)
|
for (;*c >= '0' && *c <= '9'; ++c)
|
||||||
i = 10*i + (*c - '0');
|
i = 10*i + (*c - '0');
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print literal part of format string and return next format spec
|
// Parse width or precision `n` from format string pointer `c`, and advance it
|
||||||
// position.
|
// to the next character. If an indirection is requested with `*`, the argument
|
||||||
|
// is read from `args[argIndex]` and `argIndex` is incremented (or read
|
||||||
|
// from `args[n]` in positional mode). Returns true if one or more
|
||||||
|
// characters were read.
|
||||||
|
inline bool parseWidthOrPrecision(int& n, const char*& c, bool positionalMode,
|
||||||
|
const detail::FormatArg* args,
|
||||||
|
int& argIndex, int numArgs)
|
||||||
|
{
|
||||||
|
if (*c >= '0' && *c <= '9') {
|
||||||
|
n = parseIntAndAdvance(c);
|
||||||
|
}
|
||||||
|
else if (*c == '*') {
|
||||||
|
++c;
|
||||||
|
n = 0;
|
||||||
|
if (positionalMode) {
|
||||||
|
int pos = parseIntAndAdvance(c) - 1;
|
||||||
|
if (*c != '$')
|
||||||
|
TINYFORMAT_ERROR("tinyformat: Non-positional argument used after a positional one");
|
||||||
|
if (pos >= 0 && pos < numArgs)
|
||||||
|
n = args[pos].toInt();
|
||||||
|
else
|
||||||
|
TINYFORMAT_ERROR("tinyformat: Positional argument out of range");
|
||||||
|
++c;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (argIndex < numArgs)
|
||||||
|
n = args[argIndex++].toInt();
|
||||||
|
else
|
||||||
|
TINYFORMAT_ERROR("tinyformat: Not enough arguments to read variable width or precision");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print literal part of format string and return next format spec position.
|
||||||
//
|
//
|
||||||
// Skips over any occurrences of '%%', printing a literal '%' to the
|
// Skips over any occurrences of '%%', printing a literal '%' to the output.
|
||||||
// output. The position of the first % character of the next
|
// The position of the first % character of the next nontrivial format spec is
|
||||||
// nontrivial format spec is returned, or the end of string.
|
// returned, or the end of string.
|
||||||
inline const char* printFormatStringLiteral(std::ostream& out, const char* fmt)
|
inline const char* printFormatStringLiteral(std::ostream& out, const char* fmt)
|
||||||
{
|
{
|
||||||
const char* c = fmt;
|
const char* c = fmt;
|
||||||
for(;; ++c)
|
for (;; ++c) {
|
||||||
{
|
if (*c == '\0') {
|
||||||
switch(*c)
|
out.write(fmt, c - fmt);
|
||||||
{
|
return c;
|
||||||
case '\0':
|
}
|
||||||
out.write(fmt, c - fmt);
|
else if (*c == '%') {
|
||||||
|
out.write(fmt, c - fmt);
|
||||||
|
if (*(c+1) != '%')
|
||||||
return c;
|
return c;
|
||||||
case '%':
|
// for "%%", tack trailing % onto next literal section.
|
||||||
out.write(fmt, c - fmt);
|
fmt = ++c;
|
||||||
if(*(c+1) != '%')
|
|
||||||
return c;
|
|
||||||
// for "%%", tack trailing % onto next literal section.
|
|
||||||
fmt = ++c;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -587,23 +631,43 @@ inline const char* printFormatStringLiteral(std::ostream& out, const char* fmt)
|
||||||
// Parse a format string and set the stream state accordingly.
|
// Parse a format string and set the stream state accordingly.
|
||||||
//
|
//
|
||||||
// The format mini-language recognized here is meant to be the one from C99,
|
// The format mini-language recognized here is meant to be the one from C99,
|
||||||
// with the form "%[flags][width][.precision][length]type".
|
// with the form "%[flags][width][.precision][length]type" with POSIX
|
||||||
|
// positional arguments extension.
|
||||||
|
//
|
||||||
|
// POSIX positional arguments extension:
|
||||||
|
// Conversions can be applied to the nth argument after the format in
|
||||||
|
// the argument list, rather than to the next unused argument. In this case,
|
||||||
|
// the conversion specifier character % (see below) is replaced by the sequence
|
||||||
|
// "%n$", where n is a decimal integer in the range [1,{NL_ARGMAX}],
|
||||||
|
// giving the position of the argument in the argument list. This feature
|
||||||
|
// provides for the definition of format strings that select arguments
|
||||||
|
// in an order appropriate to specific languages.
|
||||||
|
//
|
||||||
|
// The format can contain either numbered argument conversion specifications
|
||||||
|
// (that is, "%n$" and "*m$"), or unnumbered argument conversion specifications
|
||||||
|
// (that is, % and * ), but not both. The only exception to this is that %%
|
||||||
|
// can be mixed with the "%n$" form. The results of mixing numbered and
|
||||||
|
// unnumbered argument specifications in a format string are undefined.
|
||||||
|
// When numbered argument specifications are used, specifying the Nth argument
|
||||||
|
// requires that all the leading arguments, from the first to the (N-1)th,
|
||||||
|
// are specified in the format string.
|
||||||
|
//
|
||||||
|
// In format strings containing the "%n$" form of conversion specification,
|
||||||
|
// numbered arguments in the argument list can be referenced from the format
|
||||||
|
// string as many times as required.
|
||||||
//
|
//
|
||||||
// Formatting options which can't be natively represented using the ostream
|
// Formatting options which can't be natively represented using the ostream
|
||||||
// state are returned in spacePadPositive (for space padded positive numbers)
|
// state are returned in spacePadPositive (for space padded positive numbers)
|
||||||
// and ntrunc (for truncating conversions). argIndex is incremented if
|
// and ntrunc (for truncating conversions). argIndex is incremented if
|
||||||
// necessary to pull out variable width and precision. The function returns a
|
// necessary to pull out variable width and precision. The function returns a
|
||||||
// pointer to the character after the end of the current format spec.
|
// pointer to the character after the end of the current format spec.
|
||||||
inline const char* streamStateFromFormat(std::ostream& out, bool& spacePadPositive,
|
inline const char* streamStateFromFormat(std::ostream& out, bool& positionalMode,
|
||||||
|
bool& spacePadPositive,
|
||||||
int& ntrunc, const char* fmtStart,
|
int& ntrunc, const char* fmtStart,
|
||||||
const detail::FormatArg* formatters,
|
const detail::FormatArg* args,
|
||||||
int& argIndex, int numFormatters)
|
int& argIndex, int numArgs)
|
||||||
{
|
{
|
||||||
if(*fmtStart != '%')
|
TINYFORMAT_ASSERT(*fmtStart == '%');
|
||||||
{
|
|
||||||
TINYFORMAT_ERROR("tinyformat: Not enough conversion specifiers in format string");
|
|
||||||
return fmtStart;
|
|
||||||
}
|
|
||||||
// Reset stream state to defaults.
|
// Reset stream state to defaults.
|
||||||
out.width(0);
|
out.width(0);
|
||||||
out.precision(6);
|
out.precision(6);
|
||||||
|
@ -616,100 +680,113 @@ inline const char* streamStateFromFormat(std::ostream& out, bool& spacePadPositi
|
||||||
bool widthSet = false;
|
bool widthSet = false;
|
||||||
int widthExtra = 0;
|
int widthExtra = 0;
|
||||||
const char* c = fmtStart + 1;
|
const char* c = fmtStart + 1;
|
||||||
// 1) Parse flags
|
|
||||||
for(;; ++c)
|
// 1) Parse an argument index (if followed by '$') or a width possibly
|
||||||
{
|
// preceded with '0' flag.
|
||||||
switch(*c)
|
if (*c >= '0' && *c <= '9') {
|
||||||
{
|
const char tmpc = *c;
|
||||||
case '#':
|
int value = parseIntAndAdvance(c);
|
||||||
out.setf(std::ios::showpoint | std::ios::showbase);
|
if (*c == '$') {
|
||||||
continue;
|
// value is an argument index
|
||||||
case '0':
|
if (value > 0 && value <= numArgs)
|
||||||
// overridden by left alignment ('-' flag)
|
argIndex = value - 1;
|
||||||
if(!(out.flags() & std::ios::left))
|
else
|
||||||
{
|
TINYFORMAT_ERROR("tinyformat: Positional argument out of range");
|
||||||
// Use internal padding so that numeric values are
|
++c;
|
||||||
// formatted correctly, eg -00010 rather than 000-10
|
positionalMode = true;
|
||||||
out.fill('0');
|
}
|
||||||
out.setf(std::ios::internal, std::ios::adjustfield);
|
else if (positionalMode) {
|
||||||
}
|
TINYFORMAT_ERROR("tinyformat: Non-positional argument used after a positional one");
|
||||||
continue;
|
}
|
||||||
case '-':
|
else {
|
||||||
|
if (tmpc == '0') {
|
||||||
|
// Use internal padding so that numeric values are
|
||||||
|
// formatted correctly, eg -00010 rather than 000-10
|
||||||
|
out.fill('0');
|
||||||
|
out.setf(std::ios::internal, std::ios::adjustfield);
|
||||||
|
}
|
||||||
|
if (value != 0) {
|
||||||
|
// Nonzero value means that we parsed width.
|
||||||
|
widthSet = true;
|
||||||
|
out.width(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (positionalMode) {
|
||||||
|
TINYFORMAT_ERROR("tinyformat: Non-positional argument used after a positional one");
|
||||||
|
}
|
||||||
|
// 2) Parse flags and width if we did not do it in previous step.
|
||||||
|
if (!widthSet) {
|
||||||
|
// Parse flags
|
||||||
|
for (;; ++c) {
|
||||||
|
switch (*c) {
|
||||||
|
case '#':
|
||||||
|
out.setf(std::ios::showpoint | std::ios::showbase);
|
||||||
|
continue;
|
||||||
|
case '0':
|
||||||
|
// overridden by left alignment ('-' flag)
|
||||||
|
if (!(out.flags() & std::ios::left)) {
|
||||||
|
// Use internal padding so that numeric values are
|
||||||
|
// formatted correctly, eg -00010 rather than 000-10
|
||||||
|
out.fill('0');
|
||||||
|
out.setf(std::ios::internal, std::ios::adjustfield);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
case '-':
|
||||||
|
out.fill(' ');
|
||||||
|
out.setf(std::ios::left, std::ios::adjustfield);
|
||||||
|
continue;
|
||||||
|
case ' ':
|
||||||
|
// overridden by show positive sign, '+' flag.
|
||||||
|
if (!(out.flags() & std::ios::showpos))
|
||||||
|
spacePadPositive = true;
|
||||||
|
continue;
|
||||||
|
case '+':
|
||||||
|
out.setf(std::ios::showpos);
|
||||||
|
spacePadPositive = false;
|
||||||
|
widthExtra = 1;
|
||||||
|
continue;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Parse width
|
||||||
|
int width = 0;
|
||||||
|
widthSet = parseWidthOrPrecision(width, c, positionalMode,
|
||||||
|
args, argIndex, numArgs);
|
||||||
|
if (widthSet) {
|
||||||
|
if (width < 0) {
|
||||||
|
// negative widths correspond to '-' flag set
|
||||||
out.fill(' ');
|
out.fill(' ');
|
||||||
out.setf(std::ios::left, std::ios::adjustfield);
|
out.setf(std::ios::left, std::ios::adjustfield);
|
||||||
continue;
|
width = -width;
|
||||||
case ' ':
|
}
|
||||||
// overridden by show positive sign, '+' flag.
|
out.width(width);
|
||||||
if(!(out.flags() & std::ios::showpos))
|
|
||||||
spacePadPositive = true;
|
|
||||||
continue;
|
|
||||||
case '+':
|
|
||||||
out.setf(std::ios::showpos);
|
|
||||||
spacePadPositive = false;
|
|
||||||
widthExtra = 1;
|
|
||||||
continue;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
|
||||||
// 2) Parse width
|
|
||||||
if(*c >= '0' && *c <= '9')
|
|
||||||
{
|
|
||||||
widthSet = true;
|
|
||||||
out.width(parseIntAndAdvance(c));
|
|
||||||
}
|
|
||||||
if(*c == '*')
|
|
||||||
{
|
|
||||||
widthSet = true;
|
|
||||||
int width = 0;
|
|
||||||
if(argIndex < numFormatters)
|
|
||||||
width = formatters[argIndex++].toInt();
|
|
||||||
else
|
|
||||||
TINYFORMAT_ERROR("tinyformat: Not enough arguments to read variable width");
|
|
||||||
if(width < 0)
|
|
||||||
{
|
|
||||||
// negative widths correspond to '-' flag set
|
|
||||||
out.fill(' ');
|
|
||||||
out.setf(std::ios::left, std::ios::adjustfield);
|
|
||||||
width = -width;
|
|
||||||
}
|
|
||||||
out.width(width);
|
|
||||||
++c;
|
|
||||||
}
|
}
|
||||||
// 3) Parse precision
|
// 3) Parse precision
|
||||||
if(*c == '.')
|
if (*c == '.') {
|
||||||
{
|
|
||||||
++c;
|
++c;
|
||||||
int precision = 0;
|
int precision = 0;
|
||||||
if(*c == '*')
|
parseWidthOrPrecision(precision, c, positionalMode,
|
||||||
{
|
args, argIndex, numArgs);
|
||||||
++c;
|
// Presence of `.` indicates precision set, unless the inferred value
|
||||||
if(argIndex < numFormatters)
|
// was negative in which case the default is used.
|
||||||
precision = formatters[argIndex++].toInt();
|
precisionSet = precision >= 0;
|
||||||
else
|
if (precisionSet)
|
||||||
TINYFORMAT_ERROR("tinyformat: Not enough arguments to read variable precision");
|
out.precision(precision);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if(*c >= '0' && *c <= '9')
|
|
||||||
precision = parseIntAndAdvance(c);
|
|
||||||
else if(*c == '-') // negative precisions ignored, treated as zero.
|
|
||||||
parseIntAndAdvance(++c);
|
|
||||||
}
|
|
||||||
out.precision(precision);
|
|
||||||
precisionSet = true;
|
|
||||||
}
|
}
|
||||||
// 4) Ignore any C99 length modifier
|
// 4) Ignore any C99 length modifier
|
||||||
while(*c == 'l' || *c == 'h' || *c == 'L' ||
|
while (*c == 'l' || *c == 'h' || *c == 'L' ||
|
||||||
*c == 'j' || *c == 'z' || *c == 't')
|
*c == 'j' || *c == 'z' || *c == 't') {
|
||||||
++c;
|
++c;
|
||||||
|
}
|
||||||
// 5) We're up to the conversion specifier character.
|
// 5) We're up to the conversion specifier character.
|
||||||
// Set stream flags based on conversion specifier (thanks to the
|
// Set stream flags based on conversion specifier (thanks to the
|
||||||
// boost::format class for forging the way here).
|
// boost::format class for forging the way here).
|
||||||
bool intConversion = false;
|
bool intConversion = false;
|
||||||
switch(*c)
|
switch (*c) {
|
||||||
{
|
|
||||||
case 'u': case 'd': case 'i':
|
case 'u': case 'd': case 'i':
|
||||||
out.setf(std::ios::dec, std::ios::basefield);
|
out.setf(std::ios::dec, std::ios::basefield);
|
||||||
intConversion = true;
|
intConversion = true;
|
||||||
|
@ -738,6 +815,18 @@ inline const char* streamStateFromFormat(std::ostream& out, bool& spacePadPositi
|
||||||
case 'f':
|
case 'f':
|
||||||
out.setf(std::ios::fixed, std::ios::floatfield);
|
out.setf(std::ios::fixed, std::ios::floatfield);
|
||||||
break;
|
break;
|
||||||
|
case 'A':
|
||||||
|
out.setf(std::ios::uppercase);
|
||||||
|
// Falls through
|
||||||
|
case 'a':
|
||||||
|
# ifdef _MSC_VER
|
||||||
|
// Workaround https://developercommunity.visualstudio.com/content/problem/520472/hexfloat-stream-output-does-not-ignore-precision-a.html
|
||||||
|
// by always setting maximum precision on MSVC to avoid precision
|
||||||
|
// loss for doubles.
|
||||||
|
out.precision(13);
|
||||||
|
# endif
|
||||||
|
out.setf(std::ios::fixed | std::ios::scientific, std::ios::floatfield);
|
||||||
|
break;
|
||||||
case 'G':
|
case 'G':
|
||||||
out.setf(std::ios::uppercase);
|
out.setf(std::ios::uppercase);
|
||||||
// Falls through
|
// Falls through
|
||||||
|
@ -746,17 +835,13 @@ inline const char* streamStateFromFormat(std::ostream& out, bool& spacePadPositi
|
||||||
// As in boost::format, let stream decide float format.
|
// As in boost::format, let stream decide float format.
|
||||||
out.flags(out.flags() & ~std::ios::floatfield);
|
out.flags(out.flags() & ~std::ios::floatfield);
|
||||||
break;
|
break;
|
||||||
case 'a': case 'A':
|
|
||||||
TINYFORMAT_ERROR("tinyformat: the %a and %A conversion specs "
|
|
||||||
"are not supported");
|
|
||||||
break;
|
|
||||||
case 'c':
|
case 'c':
|
||||||
// Handled as special case inside formatValue()
|
// Handled as special case inside formatValue()
|
||||||
break;
|
break;
|
||||||
case 's':
|
case 's':
|
||||||
if(precisionSet)
|
if (precisionSet)
|
||||||
ntrunc = static_cast<int>(out.precision());
|
ntrunc = static_cast<int>(out.precision());
|
||||||
// Make %s print booleans as "true" and "false"
|
// Make %s print Booleans as "true" and "false"
|
||||||
out.setf(std::ios::boolalpha);
|
out.setf(std::ios::boolalpha);
|
||||||
break;
|
break;
|
||||||
case 'n':
|
case 'n':
|
||||||
|
@ -770,8 +855,7 @@ inline const char* streamStateFromFormat(std::ostream& out, bool& spacePadPositi
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if(intConversion && precisionSet && !widthSet)
|
if (intConversion && precisionSet && !widthSet) {
|
||||||
{
|
|
||||||
// "precision" for integers gives the minimum number of digits (to be
|
// "precision" for integers gives the minimum number of digits (to be
|
||||||
// padded with zeros on the left). This isn't really supported by the
|
// padded with zeros on the left). This isn't really supported by the
|
||||||
// iostreams, but we can approximately simulate it with the width if
|
// iostreams, but we can approximately simulate it with the width if
|
||||||
|
@ -786,8 +870,8 @@ inline const char* streamStateFromFormat(std::ostream& out, bool& spacePadPositi
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
inline void formatImpl(std::ostream& out, const char* fmt,
|
inline void formatImpl(std::ostream& out, const char* fmt,
|
||||||
const detail::FormatArg* formatters,
|
const detail::FormatArg* args,
|
||||||
int numFormatters)
|
int numArgs)
|
||||||
{
|
{
|
||||||
// Saved stream state
|
// Saved stream state
|
||||||
std::streamsize origWidth = out.width();
|
std::streamsize origWidth = out.width();
|
||||||
|
@ -795,26 +879,34 @@ inline void formatImpl(std::ostream& out, const char* fmt,
|
||||||
std::ios::fmtflags origFlags = out.flags();
|
std::ios::fmtflags origFlags = out.flags();
|
||||||
char origFill = out.fill();
|
char origFill = out.fill();
|
||||||
|
|
||||||
for (int argIndex = 0; argIndex < numFormatters; ++argIndex)
|
// "Positional mode" means all format specs should be of the form "%n$..."
|
||||||
{
|
// with `n` an integer. We detect this in `streamStateFromFormat`.
|
||||||
// Parse the format string
|
bool positionalMode = false;
|
||||||
|
int argIndex = 0;
|
||||||
|
while (true) {
|
||||||
fmt = printFormatStringLiteral(out, fmt);
|
fmt = printFormatStringLiteral(out, fmt);
|
||||||
|
if (*fmt == '\0') {
|
||||||
|
if (!positionalMode && argIndex < numArgs) {
|
||||||
|
TINYFORMAT_ERROR("tinyformat: Not enough conversion specifiers in format string");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
bool spacePadPositive = false;
|
bool spacePadPositive = false;
|
||||||
int ntrunc = -1;
|
int ntrunc = -1;
|
||||||
const char* fmtEnd = streamStateFromFormat(out, spacePadPositive, ntrunc, fmt,
|
const char* fmtEnd = streamStateFromFormat(out, positionalMode, spacePadPositive, ntrunc, fmt,
|
||||||
formatters, argIndex, numFormatters);
|
args, argIndex, numArgs);
|
||||||
if (argIndex >= numFormatters)
|
// NB: argIndex may be incremented by reading variable width/precision
|
||||||
{
|
// in `streamStateFromFormat`, so do the bounds check here.
|
||||||
// Check args remain after reading any variable width/precision
|
if (argIndex >= numArgs) {
|
||||||
TINYFORMAT_ERROR("tinyformat: Not enough format arguments");
|
TINYFORMAT_ERROR("tinyformat: Too many conversion specifiers in format string");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const FormatArg& arg = formatters[argIndex];
|
const FormatArg& arg = args[argIndex];
|
||||||
// Format the arg into the stream.
|
// Format the arg into the stream.
|
||||||
if(!spacePadPositive)
|
if (!spacePadPositive) {
|
||||||
arg.format(out, fmt, fmtEnd, ntrunc);
|
arg.format(out, fmt, fmtEnd, ntrunc);
|
||||||
else
|
}
|
||||||
{
|
else {
|
||||||
// The following is a special case with no direct correspondence
|
// The following is a special case with no direct correspondence
|
||||||
// between stream formatting and the printf() behaviour. Simulate
|
// between stream formatting and the printf() behaviour. Simulate
|
||||||
// it crudely by formatting into a temporary string stream and
|
// it crudely by formatting into a temporary string stream and
|
||||||
|
@ -824,18 +916,17 @@ inline void formatImpl(std::ostream& out, const char* fmt,
|
||||||
tmpStream.setf(std::ios::showpos);
|
tmpStream.setf(std::ios::showpos);
|
||||||
arg.format(tmpStream, fmt, fmtEnd, ntrunc);
|
arg.format(tmpStream, fmt, fmtEnd, ntrunc);
|
||||||
std::string result = tmpStream.str(); // allocates... yuck.
|
std::string result = tmpStream.str(); // allocates... yuck.
|
||||||
for(size_t i = 0, iend = result.size(); i < iend; ++i)
|
for (size_t i = 0, iend = result.size(); i < iend; ++i) {
|
||||||
if(result[i] == '+') result[i] = ' ';
|
if (result[i] == '+')
|
||||||
|
result[i] = ' ';
|
||||||
|
}
|
||||||
out << result;
|
out << result;
|
||||||
}
|
}
|
||||||
|
if (!positionalMode)
|
||||||
|
++argIndex;
|
||||||
fmt = fmtEnd;
|
fmt = fmtEnd;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print remaining part of format string.
|
|
||||||
fmt = printFormatStringLiteral(out, fmt);
|
|
||||||
if(*fmt != '\0')
|
|
||||||
TINYFORMAT_ERROR("tinyformat: Too many conversion specifiers in format string");
|
|
||||||
|
|
||||||
// Restore stream state
|
// Restore stream state
|
||||||
out.width(origWidth);
|
out.width(origWidth);
|
||||||
out.precision(origPrecision);
|
out.precision(origPrecision);
|
||||||
|
@ -855,14 +946,14 @@ inline void formatImpl(std::ostream& out, const char* fmt,
|
||||||
class FormatList
|
class FormatList
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
FormatList(detail::FormatArg* formatters, int N)
|
FormatList(detail::FormatArg* args, int N)
|
||||||
: m_formatters(formatters), m_N(N) { }
|
: m_args(args), m_N(N) { }
|
||||||
|
|
||||||
friend void vformat(std::ostream& out, const char* fmt,
|
friend void vformat(std::ostream& out, const char* fmt,
|
||||||
const FormatList& list);
|
const FormatList& list);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const detail::FormatArg* m_formatters;
|
const detail::FormatArg* m_args;
|
||||||
int m_N;
|
int m_N;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -879,29 +970,33 @@ class FormatListN : public FormatList
|
||||||
public:
|
public:
|
||||||
#ifdef TINYFORMAT_USE_VARIADIC_TEMPLATES
|
#ifdef TINYFORMAT_USE_VARIADIC_TEMPLATES
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
explicit FormatListN(const Args&... args)
|
FormatListN(const Args&... args)
|
||||||
: FormatList(&m_formatterStore[0], N),
|
: FormatList(&m_formatterStore[0], N),
|
||||||
m_formatterStore { FormatArg(args)... }
|
m_formatterStore { FormatArg(args)... }
|
||||||
{ static_assert(sizeof...(args) == N, "Number of args must be N"); }
|
{ static_assert(sizeof...(args) == N, "Number of args must be N"); }
|
||||||
#else // C++98 version
|
#else // C++98 version
|
||||||
void init(int) {}
|
void init(int) {}
|
||||||
# define TINYFORMAT_MAKE_FORMATLIST_CONSTRUCTOR(n) \
|
# define TINYFORMAT_MAKE_FORMATLIST_CONSTRUCTOR(n) \
|
||||||
\
|
\
|
||||||
template<TINYFORMAT_ARGTYPES(n)> \
|
template<TINYFORMAT_ARGTYPES(n)> \
|
||||||
explicit FormatListN(TINYFORMAT_VARARGS(n)) \
|
FormatListN(TINYFORMAT_VARARGS(n)) \
|
||||||
: FormatList(&m_formatterStore[0], n) \
|
: FormatList(&m_formatterStore[0], n) \
|
||||||
{ assert(n == N); init(0, TINYFORMAT_PASSARGS(n)); } \
|
{ TINYFORMAT_ASSERT(n == N); init(0, TINYFORMAT_PASSARGS(n)); } \
|
||||||
\
|
\
|
||||||
template<TINYFORMAT_ARGTYPES(n)> \
|
template<TINYFORMAT_ARGTYPES(n)> \
|
||||||
void init(int i, TINYFORMAT_VARARGS(n)) \
|
void init(int i, TINYFORMAT_VARARGS(n)) \
|
||||||
{ \
|
{ \
|
||||||
m_formatterStore[i] = FormatArg(v1); \
|
m_formatterStore[i] = FormatArg(v1); \
|
||||||
init(i+1 TINYFORMAT_PASSARGS_TAIL(n)); \
|
init(i+1 TINYFORMAT_PASSARGS_TAIL(n)); \
|
||||||
}
|
}
|
||||||
|
|
||||||
TINYFORMAT_FOREACH_ARGNUM(TINYFORMAT_MAKE_FORMATLIST_CONSTRUCTOR)
|
TINYFORMAT_FOREACH_ARGNUM(TINYFORMAT_MAKE_FORMATLIST_CONSTRUCTOR)
|
||||||
# undef TINYFORMAT_MAKE_FORMATLIST_CONSTRUCTOR
|
# undef TINYFORMAT_MAKE_FORMATLIST_CONSTRUCTOR
|
||||||
#endif
|
#endif
|
||||||
|
FormatListN(const FormatListN& other)
|
||||||
|
: FormatList(&m_formatterStore[0], N)
|
||||||
|
{ std::copy(&other.m_formatterStore[0], &other.m_formatterStore[N],
|
||||||
|
&m_formatterStore[0]); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
FormatArg m_formatterStore[N];
|
FormatArg m_formatterStore[N];
|
||||||
|
@ -956,7 +1051,7 @@ TINYFORMAT_FOREACH_ARGNUM(TINYFORMAT_MAKE_MAKEFORMATLIST)
|
||||||
/// list of format arguments is held in a single function argument.
|
/// list of format arguments is held in a single function argument.
|
||||||
inline void vformat(std::ostream& out, const char* fmt, FormatListRef list)
|
inline void vformat(std::ostream& out, const char* fmt, FormatListRef list)
|
||||||
{
|
{
|
||||||
detail::formatImpl(out, fmt, list.m_formatters, list.m_N);
|
detail::formatImpl(out, fmt, list.m_args, list.m_N);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -993,6 +1088,7 @@ void printfln(const char* fmt, const Args&... args)
|
||||||
std::cout << '\n';
|
std::cout << '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#else // C++98 version
|
#else // C++98 version
|
||||||
|
|
||||||
inline void format(std::ostream& out, const char* fmt)
|
inline void format(std::ostream& out, const char* fmt)
|
||||||
|
@ -1063,6 +1159,7 @@ std::string format(const std::string &fmt, const Args&... args)
|
||||||
|
|
||||||
} // namespace tinyformat
|
} // namespace tinyformat
|
||||||
|
|
||||||
|
// Added for Bitcoin Core:
|
||||||
/** Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for details) */
|
/** Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for details) */
|
||||||
#define strprintf tfm::format
|
#define strprintf tfm::format
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue