Commit ded0a3bb authored by Victor Zverovich's avatar Victor Zverovich

Internalize undocumented basic_writer

parent 83174f2a
......@@ -803,7 +803,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
basic_memory_buffer<Char> buf;
auto out = std::back_inserter(buf);
using range = internal::output_range<decltype(ctx.out()), Char>;
basic_writer<range> w(range(ctx.out()));
internal::basic_writer<range> w(range(ctx.out()));
internal::handle_dynamic_spec<internal::width_checker>(
spec.width_, width_ref, ctx, format_str.begin());
internal::handle_dynamic_spec<internal::precision_checker>(
......
......@@ -164,7 +164,7 @@ FMT_FUNC void format_error_code(internal::buffer<char>& out, int error_code,
++error_code_size;
}
error_code_size += internal::to_unsigned(internal::count_digits(abs_value));
writer w(out);
internal::writer w(out);
if (message.size() <= inline_buffer_size - error_code_size) {
w.write(message);
w.write(SEP);
......@@ -245,10 +245,9 @@ template <typename T>
int format_float(char* buf, std::size_t size, const char* format, int precision,
T value) {
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
if (precision > 100000) {
if (precision > 100000)
throw std::runtime_error(
"fuzz mode - avoid large allocation inside snprintf");
}
#endif
// Suppress the warning about nonliteral format string.
auto snprintf_ptr = FMT_SNPRINTF;
......@@ -898,7 +897,7 @@ FMT_FUNC void internal::format_windows_error(internal::buffer<char>& out,
if (result != 0) {
utf16_to_utf8 utf8_message;
if (utf8_message.convert(system_message) == ERROR_SUCCESS) {
writer w(out);
internal::writer w(out);
w.write(message);
w.write(": ");
w.write(utf8_message);
......@@ -927,7 +926,7 @@ FMT_FUNC void format_system_error(internal::buffer<char>& out, int error_code,
int result =
internal::safe_strerror(error_code, system_message, buf.size());
if (result == 0) {
writer w(out);
internal::writer w(out);
w.write(message);
w.write(": ");
w.write(system_message);
......
......@@ -300,7 +300,7 @@ enum { inline_buffer_size = 500 };
A dynamically growing memory buffer for trivially copyable/constructible types
with the first ``SIZE`` elements stored in the object itself.
You can use one of the following typedefs for common character types:
You can use one of the following type aliases for common character types:
+----------------+------------------------------+
| Type | Definition |
......@@ -340,8 +340,8 @@ class basic_memory_buffer : private Allocator, public internal::buffer<T> {
void grow(std::size_t size) FMT_OVERRIDE;
public:
typedef T value_type;
typedef const T& const_reference;
using value_type = T;
using const_reference = const T&;
explicit basic_memory_buffer(const Allocator& alloc = Allocator())
: Allocator(alloc) {
......@@ -397,9 +397,7 @@ class basic_memory_buffer : private Allocator, public internal::buffer<T> {
template <typename T, std::size_t SIZE, typename Allocator>
void basic_memory_buffer<T, SIZE, Allocator>::grow(std::size_t size) {
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
if (size > 1000) {
throw std::runtime_error("fuzz mode - won't grow that much");
}
if (size > 1000) throw std::runtime_error("fuzz mode - won't grow that much");
#endif
std::size_t old_capacity = this->capacity();
std::size_t new_capacity = old_capacity + old_capacity / 2;
......@@ -416,8 +414,8 @@ void basic_memory_buffer<T, SIZE, Allocator>::grow(std::size_t size) {
if (old_data != store_) Allocator::deallocate(old_data, old_capacity);
}
typedef basic_memory_buffer<char> memory_buffer;
typedef basic_memory_buffer<wchar_t> wmemory_buffer;
using memory_buffer = basic_memory_buffer<char>;
using wmemory_buffer = basic_memory_buffer<wchar_t>;
/** A formatting error such as invalid format string. */
class FMT_API format_error : public std::runtime_error {
......@@ -428,10 +426,6 @@ class FMT_API format_error : public std::runtime_error {
~format_error() FMT_NOEXCEPT;
};
template <typename Range> class basic_writer;
using writer = basic_writer<internal::buffer_range<char>>;
using wwriter = basic_writer<internal::buffer_range<wchar_t>>;
namespace internal {
// A workaround for std::string not having mutable data() until C++17.
......@@ -1069,9 +1063,8 @@ It grisu_prettify(const char* digits, int size, int exp, It it,
if (params.trailing_zeros) {
*it++ = static_cast<Char>('.');
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
if (num_zeros > 1000) {
if (num_zeros > 1000)
throw std::runtime_error("fuzz mode - avoiding excessive cpu use");
}
#endif
it = std::fill_n(it, num_zeros, static_cast<Char>('0'));
}
......@@ -1271,836 +1264,914 @@ void arg_map<Context>::init(const basic_format_args<Context>& args) {
}
}
template <typename Range, typename ErrorHandler = internal::error_handler>
class arg_formatter_base {
/**
This template provides operations for formatting and writing data into a
character range.
*/
template <typename Range> class basic_writer {
public:
typedef typename Range::value_type char_type;
typedef decltype(std::declval<Range>().begin()) iterator;
typedef basic_format_specs<char_type> format_specs;
private:
typedef basic_writer<Range> writer_type;
writer_type writer_;
format_specs* specs_;
iterator out_; // Output iterator.
internal::locale_ref locale_;
struct char_writer {
char_type value;
// Attempts to reserve space for n extra characters in the output range.
// Returns a pointer to the reserved range or a reference to out_.
auto reserve(std::size_t n) -> decltype(internal::reserve(out_, n)) {
return internal::reserve(out_, n);
}
size_t size() const { return 1; }
size_t width() const { return 1; }
template <typename F> struct padded_int_writer {
size_t size_;
string_view prefix;
char_type fill;
std::size_t padding;
F f;
template <typename It> void operator()(It&& it) const { *it++ = value; }
size_t size() const { return size_; }
size_t width() const { return size_; }
template <typename It> void operator()(It&& it) const {
if (prefix.size() != 0)
it = internal::copy_str<char_type>(prefix.begin(), prefix.end(), it);
it = std::fill_n(it, padding, fill);
f(it);
}
};
void write_char(char_type value) {
if (specs_)
writer_.write_padded(*specs_, char_writer{value});
else
writer_.write(value);
// Writes an integer in the format
// <left-padding><prefix><numeric-padding><digits><right-padding>
// where <digits> are written by f(it).
template <typename Spec, typename F>
void write_int(int num_digits, string_view prefix, const Spec& spec, F f) {
std::size_t size = prefix.size() + internal::to_unsigned(num_digits);
char_type fill = static_cast<char_type>(spec.fill());
std::size_t padding = 0;
if (spec.align() == ALIGN_NUMERIC) {
if (spec.width() > size) {
padding = spec.width() - size;
size = spec.width();
}
} else if (spec.precision > num_digits) {
size = prefix.size() + internal::to_unsigned(spec.precision);
padding = internal::to_unsigned(spec.precision - num_digits);
fill = static_cast<char_type>('0');
}
align_spec as = spec;
if (spec.align() == ALIGN_DEFAULT) as.align_ = ALIGN_RIGHT;
write_padded(as, padded_int_writer<F>{size, prefix, fill, padding, f});
}
void write_pointer(const void* p) {
writer_.write_pointer(internal::bit_cast<internal::uintptr_t>(p), specs_);
// Writes a decimal integer.
template <typename Int> void write_decimal(Int value) {
typedef typename internal::int_traits<Int>::main_type main_type;
main_type abs_value = static_cast<main_type>(value);
bool is_negative = internal::is_negative(value);
if (is_negative) abs_value = 0 - abs_value;
int num_digits = internal::count_digits(abs_value);
auto&& it =
reserve((is_negative ? 1 : 0) + static_cast<size_t>(num_digits));
if (is_negative) *it++ = static_cast<char_type>('-');
it = internal::format_decimal<char_type>(it, abs_value, num_digits);
}
protected:
writer_type& writer() { return writer_; }
format_specs* spec() { return specs_; }
iterator out() { return writer_.out(); }
// The handle_int_type_spec handler that writes an integer.
template <typename Int, typename Spec> struct int_writer {
typedef typename internal::int_traits<Int>::main_type unsigned_type;
void write(bool value) {
string_view sv(value ? "true" : "false");
specs_ ? writer_.write(sv, *specs_) : writer_.write(sv);
}
basic_writer<Range>& writer;
const Spec& spec;
unsigned_type abs_value;
char prefix[4];
unsigned prefix_size;
void write(const char_type* value) {
if (!value) FMT_THROW(format_error("string pointer is null"));
auto length = std::char_traits<char_type>::length(value);
basic_string_view<char_type> sv(value, length);
specs_ ? writer_.write(sv, *specs_) : writer_.write(sv);
}
string_view get_prefix() const { return string_view(prefix, prefix_size); }
public:
arg_formatter_base(Range r, format_specs* s, locale_ref loc)
: writer_(r, loc), specs_(s) {}
int_writer(basic_writer<Range>& w, Int value, const Spec& s)
: writer(w),
spec(s),
abs_value(static_cast<unsigned_type>(value)),
prefix_size(0) {
if (internal::is_negative(value)) {
prefix[0] = '-';
++prefix_size;
abs_value = 0 - abs_value;
} else if (spec.has(SIGN_FLAG)) {
prefix[0] = spec.has(PLUS_FLAG) ? '+' : ' ';
++prefix_size;
}
}
iterator operator()(monostate) {
FMT_ASSERT(false, "invalid argument type");
return out();
}
struct dec_writer {
unsigned_type abs_value;
int num_digits;
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
iterator operator()(T value) {
if (specs_)
writer_.write_int(value, *specs_);
else
writer_.write(value);
return out();
}
template <typename It> void operator()(It&& it) const {
it = internal::format_decimal<char_type>(it, abs_value, num_digits);
}
};
iterator operator()(char_type value) {
internal::handle_char_specs(
specs_, char_spec_handler(*this, static_cast<char_type>(value)));
return out();
}
void on_dec() {
int num_digits = internal::count_digits(abs_value);
writer.write_int(num_digits, get_prefix(), spec,
dec_writer{abs_value, num_digits});
}
iterator operator()(bool value) {
if (specs_ && specs_->type) return (*this)(value ? 1 : 0);
write(value != 0);
return out();
}
struct hex_writer {
int_writer& self;
int num_digits;
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
iterator operator()(T value) {
writer_.write_double(value, specs_ ? *specs_ : format_specs());
return out();
}
template <typename It> void operator()(It&& it) const {
it = internal::format_uint<4, char_type>(it, self.abs_value, num_digits,
self.spec.type != 'x');
}
};
struct char_spec_handler : ErrorHandler {
arg_formatter_base& formatter;
char_type value;
void on_hex() {
if (spec.has(HASH_FLAG)) {
prefix[prefix_size++] = '0';
prefix[prefix_size++] = static_cast<char>(spec.type);
}
int num_digits = internal::count_digits<4>(abs_value);
writer.write_int(num_digits, get_prefix(), spec,
hex_writer{*this, num_digits});
}
char_spec_handler(arg_formatter_base& f, char_type val)
: formatter(f), value(val) {}
template <int BITS> struct bin_writer {
unsigned_type abs_value;
int num_digits;
void on_int() {
if (formatter.specs_)
formatter.writer_.write_int(value, *formatter.specs_);
else
formatter.writer_.write(value);
template <typename It> void operator()(It&& it) const {
it = internal::format_uint<BITS, char_type>(it, abs_value, num_digits);
}
};
void on_bin() {
if (spec.has(HASH_FLAG)) {
prefix[prefix_size++] = '0';
prefix[prefix_size++] = static_cast<char>(spec.type);
}
int num_digits = internal::count_digits<1>(abs_value);
writer.write_int(num_digits, get_prefix(), spec,
bin_writer<1>{abs_value, num_digits});
}
void on_char() { formatter.write_char(value); }
};
struct cstring_spec_handler : internal::error_handler {
arg_formatter_base& formatter;
const char_type* value;
void on_oct() {
int num_digits = internal::count_digits<3>(abs_value);
if (spec.has(HASH_FLAG) && spec.precision <= num_digits) {
// Octal prefix '0' is counted as a digit, so only add it if precision
// is not greater than the number of digits.
prefix[prefix_size++] = '0';
}
writer.write_int(num_digits, get_prefix(), spec,
bin_writer<3>{abs_value, num_digits});
}
cstring_spec_handler(arg_formatter_base& f, const char_type* val)
: formatter(f), value(val) {}
enum { SEP_SIZE = 1 };
void on_string() { formatter.write(value); }
void on_pointer() { formatter.write_pointer(value); }
};
struct num_writer {
unsigned_type abs_value;
int size;
char_type sep;
iterator operator()(const char_type* value) {
if (!specs_) return write(value), out();
internal::handle_cstring_type_spec(specs_->type,
cstring_spec_handler(*this, value));
return out();
}
template <typename It> void operator()(It&& it) const {
basic_string_view<char_type> s(&sep, SEP_SIZE);
it = internal::format_decimal<char_type>(
it, abs_value, size, internal::add_thousands_sep<char_type>(s));
}
};
iterator operator()(basic_string_view<char_type> value) {
if (specs_) {
internal::check_string_type_spec(specs_->type, internal::error_handler());
writer_.write(value, *specs_);
} else {
writer_.write(value);
void on_num() {
int num_digits = internal::count_digits(abs_value);
char_type sep = internal::thousands_sep<char_type>(writer.locale_);
int size = num_digits + SEP_SIZE * ((num_digits - 1) / 3);
writer.write_int(size, get_prefix(), spec,
num_writer{abs_value, size, sep});
}
return out();
}
iterator operator()(const void* value) {
if (specs_)
check_pointer_type_spec(specs_->type, internal::error_handler());
write_pointer(value);
return out();
}
};
FMT_NORETURN void on_error() {
FMT_THROW(format_error("invalid type specifier"));
}
};
template <typename Char> FMT_CONSTEXPR bool is_name_start(Char c) {
return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || '_' == c;
}
enum { INF_SIZE = 3 }; // This is an enum to workaround a bug in MSVC.
// Parses the range [begin, end) as an unsigned integer. This function assumes
// that the range is non-empty and the first character is a digit.
template <typename Char, typename ErrorHandler>
FMT_CONSTEXPR unsigned parse_nonnegative_int(const Char*& begin,
const Char* end,
ErrorHandler&& eh) {
assert(begin != end && '0' <= *begin && *begin <= '9');
if (*begin == '0') {
++begin;
return 0;
}
unsigned value = 0;
// Convert to unsigned to prevent a warning.
unsigned max_int = (std::numeric_limits<int>::max)();
unsigned big = max_int / 10;
do {
// Check for overflow.
if (value > big) {
value = max_int + 1;
break;
struct inf_or_nan_writer {
char sign;
bool as_percentage;
const char* str;
size_t size() const {
return static_cast<std::size_t>(INF_SIZE + (sign ? 1 : 0) +
(as_percentage ? 1 : 0));
}
value = value * 10 + unsigned(*begin - '0');
++begin;
} while (begin != end && '0' <= *begin && *begin <= '9');
if (value > max_int) eh.on_error("number is too big");
return value;
}
size_t width() const { return size(); }
template <typename Context> class custom_formatter {
private:
typedef typename Context::char_type char_type;
template <typename It> void operator()(It&& it) const {
if (sign) *it++ = static_cast<char_type>(sign);
it = internal::copy_str<char_type>(
str, str + static_cast<std::size_t>(INF_SIZE), it);
if (as_percentage) *it++ = static_cast<char_type>('%');
}
};
basic_parse_context<char_type>& parse_ctx_;
Context& ctx_;
struct double_writer {
char sign;
internal::buffer<char>& buffer;
public:
explicit custom_formatter(basic_parse_context<char_type>& parse_ctx,
Context& ctx)
: parse_ctx_(parse_ctx), ctx_(ctx) {}
size_t size() const { return buffer.size() + (sign ? 1 : 0); }
size_t width() const { return size(); }
bool operator()(typename basic_format_arg<Context>::handle h) const {
h.format(parse_ctx_, ctx_);
return true;
}
template <typename It> void operator()(It&& it) {
if (sign) *it++ = static_cast<char_type>(sign);
it = internal::copy_str<char_type>(buffer.begin(), buffer.end(), it);
}
};
template <typename T> bool operator()(T) const { return false; }
};
class grisu_writer {
private:
internal::buffer<char>& digits_;
size_t size_;
char sign_;
int exp_;
internal::gen_digits_params params_;
template <typename T> struct is_integer {
enum {
value = std::is_integral<T>::value && !std::is_same<T, bool>::value &&
!std::is_same<T, char>::value && !std::is_same<T, wchar_t>::value
public:
grisu_writer(char sign, internal::buffer<char>& digits, int exp,
const internal::gen_digits_params& params)
: digits_(digits), sign_(sign), exp_(exp), params_(params) {
int num_digits = static_cast<int>(digits.size());
int full_exp = num_digits + exp - 1;
int precision = params.num_digits > 0 ? params.num_digits : 11;
params_.fixed |= full_exp >= -4 && full_exp < precision;
auto it = internal::grisu_prettify<char>(
digits.data(), num_digits, exp, internal::counting_iterator<char>(),
params_);
size_ = it.count();
}
size_t size() const { return size_ + (sign_ ? 1 : 0); }
size_t width() const { return size(); }
template <typename It> void operator()(It&& it) {
if (sign_) *it++ = static_cast<char_type>(sign_);
int num_digits = static_cast<int>(digits_.size());
it = internal::grisu_prettify<char_type>(digits_.data(), num_digits, exp_,
it, params_);
}
};
};
template <typename ErrorHandler> class width_checker {
public:
explicit FMT_CONSTEXPR width_checker(ErrorHandler& eh) : handler_(eh) {}
template <typename Char> struct str_writer {
const Char* s;
size_t size_;
template <typename T, FMT_ENABLE_IF(is_integer<T>::value)>
FMT_CONSTEXPR unsigned long long operator()(T value) {
if (is_negative(value)) handler_.on_error("negative width");
return static_cast<unsigned long long>(value);
}
size_t size() const { return size_; }
size_t width() const {
return internal::count_code_points(basic_string_view<Char>(s, size_));
}
template <typename T, FMT_ENABLE_IF(!is_integer<T>::value)>
FMT_CONSTEXPR unsigned long long operator()(T) {
handler_.on_error("width is not integer");
return 0;
}
template <typename It> void operator()(It&& it) const {
it = internal::copy_str<char_type>(s, s + size_, it);
}
};
private:
ErrorHandler& handler_;
};
template <typename UIntPtr> struct pointer_writer {
UIntPtr value;
int num_digits;
size_t size() const { return num_digits + 2; }
size_t width() const { return size(); }
template <typename It> void operator()(It&& it) const {
*it++ = static_cast<char_type>('0');
*it++ = static_cast<char_type>('x');
it = internal::format_uint<4, char_type>(it, value, num_digits);
}
};
template <typename ErrorHandler> class precision_checker {
public:
explicit FMT_CONSTEXPR precision_checker(ErrorHandler& eh) : handler_(eh) {}
/** Constructs a ``basic_writer`` object. */
explicit basic_writer(Range out,
internal::locale_ref loc = internal::locale_ref())
: out_(out.begin()), locale_(loc) {}
template <typename T, FMT_ENABLE_IF(is_integer<T>::value)>
FMT_CONSTEXPR unsigned long long operator()(T value) {
if (is_negative(value)) handler_.on_error("negative precision");
return static_cast<unsigned long long>(value);
}
iterator out() const { return out_; }
template <typename T, FMT_ENABLE_IF(!is_integer<T>::value)>
FMT_CONSTEXPR unsigned long long operator()(T) {
handler_.on_error("precision is not integer");
return 0;
// Writes a value in the format
// <left-padding><value><right-padding>
// where <value> is written by f(it).
template <typename F> void write_padded(const align_spec& spec, F&& f) {
unsigned width = spec.width(); // User-perceived width (in code points).
size_t size = f.size(); // The number of code units.
size_t num_code_points = width != 0 ? f.width() : size;
if (width <= num_code_points) return f(reserve(size));
auto&& it = reserve(width + (size - num_code_points));
char_type fill = static_cast<char_type>(spec.fill());
std::size_t padding = width - num_code_points;
if (spec.align() == ALIGN_RIGHT) {
it = std::fill_n(it, padding, fill);
f(it);
} else if (spec.align() == ALIGN_CENTER) {
std::size_t left_padding = padding / 2;
it = std::fill_n(it, left_padding, fill);
f(it);
it = std::fill_n(it, padding - left_padding, fill);
} else {
f(it);
it = std::fill_n(it, padding, fill);
}
}
private:
ErrorHandler& handler_;
};
// A format specifier handler that sets fields in basic_format_specs.
template <typename Char> class specs_setter {
public:
explicit FMT_CONSTEXPR specs_setter(basic_format_specs<Char>& specs)
: specs_(specs) {}
void write(int value) { write_decimal(value); }
void write(long value) { write_decimal(value); }
void write(long long value) { write_decimal(value); }
FMT_CONSTEXPR specs_setter(const specs_setter& other)
: specs_(other.specs_) {}
void write(unsigned value) { write_decimal(value); }
void write(unsigned long value) { write_decimal(value); }
void write(unsigned long long value) { write_decimal(value); }
FMT_CONSTEXPR void on_align(alignment align) { specs_.align_ = align; }
FMT_CONSTEXPR void on_fill(Char fill) { specs_.fill_ = fill; }
FMT_CONSTEXPR void on_plus() { specs_.flags |= SIGN_FLAG | PLUS_FLAG; }
FMT_CONSTEXPR void on_minus() { specs_.flags |= MINUS_FLAG; }
FMT_CONSTEXPR void on_space() { specs_.flags |= SIGN_FLAG; }
FMT_CONSTEXPR void on_hash() { specs_.flags |= HASH_FLAG; }
// Writes a formatted integer.
template <typename T, typename Spec>
void write_int(T value, const Spec& spec) {
internal::handle_int_type_spec(spec.type,
int_writer<T, Spec>(*this, value, spec));
}
FMT_CONSTEXPR void on_zero() {
specs_.align_ = ALIGN_NUMERIC;
specs_.fill_ = '0';
/**
\rst
Formats *value* and writes it to the buffer.
\endrst
*/
template <typename T, typename FormatSpec, typename... FormatSpecs,
FMT_ENABLE_IF(std::is_integral<T>::value)>
void write(T value, FormatSpec spec, FormatSpecs... specs) {
format_specs s(spec, specs...);
s.align_ = ALIGN_RIGHT;
write_int(value, s);
}
FMT_CONSTEXPR void on_width(unsigned width) { specs_.width_ = width; }
FMT_CONSTEXPR void on_precision(unsigned precision) {
specs_.precision = static_cast<int>(precision);
void write(double value, const format_specs& spec = format_specs()) {
write_double(value, spec);
}
FMT_CONSTEXPR void end_precision() {}
FMT_CONSTEXPR void on_type(Char type) {
specs_.type = static_cast<char>(type);
/**
\rst
Formats *value* using the general format for floating-point numbers
(``'g'``) and writes it to the buffer.
\endrst
*/
void write(long double value, const format_specs& spec = format_specs()) {
write_double(value, spec);
}
protected:
basic_format_specs<Char>& specs_;
};
template <typename ErrorHandler> class numeric_specs_checker {
public:
FMT_CONSTEXPR numeric_specs_checker(ErrorHandler& eh, internal::type arg_type)
: error_handler_(eh), arg_type_(arg_type) {}
FMT_CONSTEXPR void require_numeric_argument() {
if (!is_arithmetic(arg_type_))
error_handler_.on_error("format specifier requires numeric argument");
}
// Formats a floating-point number (double or long double).
template <typename T> void write_double(T value, const format_specs& spec);
FMT_CONSTEXPR void check_sign() {
require_numeric_argument();
if (is_integral(arg_type_) && arg_type_ != int_type &&
arg_type_ != long_long_type && arg_type_ != internal::char_type) {
error_handler_.on_error("format specifier requires signed argument");
}
/** Writes a character to the buffer. */
void write(char value) {
auto&& it = reserve(1);
*it++ = value;
}
FMT_CONSTEXPR void check_precision() {
if (is_integral(arg_type_) || arg_type_ == internal::pointer_type)
error_handler_.on_error("precision not allowed for this argument type");
template <typename Char, FMT_ENABLE_IF(std::is_same<Char, char_type>::value)>
void write(Char value) {
auto&& it = reserve(1);
*it++ = value;
}
private:
ErrorHandler& error_handler_;
internal::type arg_type_;
};
// A format specifier handler that checks if specifiers are consistent with the
// argument type.
template <typename Handler> class specs_checker : public Handler {
public:
FMT_CONSTEXPR specs_checker(const Handler& handler, internal::type arg_type)
: Handler(handler), checker_(*this, arg_type) {}
FMT_CONSTEXPR specs_checker(const specs_checker& other)
: Handler(other), checker_(*this, other.arg_type_) {}
FMT_CONSTEXPR void on_align(alignment align) {
if (align == ALIGN_NUMERIC) checker_.require_numeric_argument();
Handler::on_align(align);
/**
\rst
Writes *value* to the buffer.
\endrst
*/
void write(string_view value) {
auto&& it = reserve(value.size());
it = internal::copy_str<char_type>(value.begin(), value.end(), it);
}
FMT_CONSTEXPR void on_plus() {
checker_.check_sign();
Handler::on_plus();
void write(wstring_view value) {
static_assert(std::is_same<char_type, wchar_t>::value, "");
auto&& it = reserve(value.size());
it = std::copy(value.begin(), value.end(), it);
}
FMT_CONSTEXPR void on_minus() {
checker_.check_sign();
Handler::on_minus();
// Writes a formatted string.
template <typename Char>
void write(const Char* s, std::size_t size, const align_spec& spec) {
write_padded(spec, str_writer<Char>{s, size});
}
FMT_CONSTEXPR void on_space() {
checker_.check_sign();
Handler::on_space();
template <typename Char>
void write(basic_string_view<Char> s,
const format_specs& spec = format_specs()) {
const Char* data = s.data();
std::size_t size = s.size();
if (spec.precision >= 0 && internal::to_unsigned(spec.precision) < size)
size = internal::to_unsigned(spec.precision);
write(data, size, spec);
}
FMT_CONSTEXPR void on_hash() {
checker_.require_numeric_argument();
Handler::on_hash();
template <typename UIntPtr>
void write_pointer(UIntPtr value, const align_spec* spec) {
int num_digits = internal::count_digits<4>(value);
auto pw = pointer_writer<UIntPtr>{value, num_digits};
if (!spec) return pw(reserve(num_digits + 2));
align_spec as = *spec;
if (as.align() == ALIGN_DEFAULT) as.align_ = ALIGN_RIGHT;
write_padded(as, pw);
}
};
FMT_CONSTEXPR void on_zero() {
checker_.require_numeric_argument();
Handler::on_zero();
}
using writer = basic_writer<buffer_range<char>>;
FMT_CONSTEXPR void end_precision() { checker_.check_precision(); }
template <typename Range, typename ErrorHandler = internal::error_handler>
class arg_formatter_base {
public:
typedef typename Range::value_type char_type;
typedef decltype(std::declval<Range>().begin()) iterator;
typedef basic_format_specs<char_type> format_specs;
private:
numeric_specs_checker<Handler> checker_;
};
template <template <typename> class Handler, typename T, typename FormatArg,
typename ErrorHandler>
FMT_CONSTEXPR void set_dynamic_spec(T& value, FormatArg arg, ErrorHandler eh) {
unsigned long long big_value =
visit_format_arg(Handler<ErrorHandler>(eh), arg);
if (big_value > to_unsigned((std::numeric_limits<int>::max)()))
eh.on_error("number is too big");
value = static_cast<T>(big_value);
}
typedef basic_writer<Range> writer_type;
writer_type writer_;
format_specs* specs_;
struct auto_id {};
struct char_writer {
char_type value;
template <typename Context>
FMT_CONSTEXPR typename Context::format_arg get_arg(Context& ctx, unsigned id) {
auto arg = ctx.arg(id);
if (!arg) ctx.on_error("argument index out of range");
return arg;
}
size_t size() const { return 1; }
size_t width() const { return 1; }
// The standard format specifier handler with checking.
template <typename ParseContext, typename Context>
class specs_handler : public specs_setter<typename Context::char_type> {
public:
typedef typename Context::char_type char_type;
template <typename It> void operator()(It&& it) const { *it++ = value; }
};
FMT_CONSTEXPR specs_handler(basic_format_specs<char_type>& specs,
ParseContext& parse_ctx, Context& ctx)
: specs_setter<char_type>(specs),
parse_context_(parse_ctx),
context_(ctx) {}
void write_char(char_type value) {
if (specs_)
writer_.write_padded(*specs_, char_writer{value});
else
writer_.write(value);
}
template <typename Id> FMT_CONSTEXPR void on_dynamic_width(Id arg_id) {
set_dynamic_spec<width_checker>(this->specs_.width_, get_arg(arg_id),
context_.error_handler());
void write_pointer(const void* p) {
writer_.write_pointer(internal::bit_cast<internal::uintptr_t>(p), specs_);
}
template <typename Id> FMT_CONSTEXPR void on_dynamic_precision(Id arg_id) {
set_dynamic_spec<precision_checker>(this->specs_.precision, get_arg(arg_id),
context_.error_handler());
protected:
writer_type& writer() { return writer_; }
format_specs* spec() { return specs_; }
iterator out() { return writer_.out(); }
void write(bool value) {
string_view sv(value ? "true" : "false");
specs_ ? writer_.write(sv, *specs_) : writer_.write(sv);
}
void on_error(const char* message) { context_.on_error(message); }
void write(const char_type* value) {
if (!value) FMT_THROW(format_error("string pointer is null"));
auto length = std::char_traits<char_type>::length(value);
basic_string_view<char_type> sv(value, length);
specs_ ? writer_.write(sv, *specs_) : writer_.write(sv);
}
private:
// This is only needed for compatibility with gcc 4.4.
typedef typename Context::format_arg format_arg;
public:
arg_formatter_base(Range r, format_specs* s, locale_ref loc)
: writer_(r, loc), specs_(s) {}
FMT_CONSTEXPR format_arg get_arg(auto_id) {
return internal::get_arg(context_, parse_context_.next_arg_id());
iterator operator()(monostate) {
FMT_ASSERT(false, "invalid argument type");
return out();
}
FMT_CONSTEXPR format_arg get_arg(unsigned arg_id) {
parse_context_.check_arg_id(arg_id);
return internal::get_arg(context_, arg_id);
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
iterator operator()(T value) {
if (specs_)
writer_.write_int(value, *specs_);
else
writer_.write(value);
return out();
}
FMT_CONSTEXPR format_arg get_arg(basic_string_view<char_type> arg_id) {
parse_context_.check_arg_id(arg_id);
return context_.arg(arg_id);
iterator operator()(char_type value) {
internal::handle_char_specs(
specs_, char_spec_handler(*this, static_cast<char_type>(value)));
return out();
}
ParseContext& parse_context_;
Context& context_;
};
iterator operator()(bool value) {
if (specs_ && specs_->type) return (*this)(value ? 1 : 0);
write(value != 0);
return out();
}
struct string_view_metadata {
FMT_CONSTEXPR string_view_metadata() : offset_(0u), size_(0u) {}
template <typename Char>
FMT_CONSTEXPR string_view_metadata(basic_string_view<Char> primary_string,
basic_string_view<Char> view)
: offset_(view.data() - primary_string.data()), size_(view.size()) {}
FMT_CONSTEXPR string_view_metadata(std::size_t offset, std::size_t size)
: offset_(offset), size_(size) {}
template <typename Char>
FMT_CONSTEXPR basic_string_view<Char> to_view(const Char* str) const {
return {str + offset_, size_};
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
iterator operator()(T value) {
writer_.write_double(value, specs_ ? *specs_ : format_specs());
return out();
}
std::size_t offset_;
std::size_t size_;
};
struct char_spec_handler : ErrorHandler {
arg_formatter_base& formatter;
char_type value;
// An argument reference.
template <typename Char> struct arg_ref {
enum Kind { NONE, INDEX, NAME };
typedef Char char_type;
char_spec_handler(arg_formatter_base& f, char_type val)
: formatter(f), value(val) {}
FMT_CONSTEXPR arg_ref() : kind(NONE), val() {}
FMT_CONSTEXPR explicit arg_ref(unsigned index) : kind(INDEX), val(index) {}
FMT_CONSTEXPR explicit arg_ref(string_view_metadata name)
: kind(NAME), val(name) {}
void on_int() {
if (formatter.specs_)
formatter.writer_.write_int(value, *formatter.specs_);
else
formatter.writer_.write(value);
}
void on_char() { formatter.write_char(value); }
};
FMT_CONSTEXPR arg_ref& operator=(unsigned idx) {
kind = INDEX;
val.index = idx;
return *this;
}
struct cstring_spec_handler : internal::error_handler {
arg_formatter_base& formatter;
const char_type* value;
Kind kind;
union value {
FMT_CONSTEXPR value() : index(0u) {}
FMT_CONSTEXPR value(unsigned id) : index(id) {}
FMT_CONSTEXPR value(string_view_metadata n) : name(n) {}
cstring_spec_handler(arg_formatter_base& f, const char_type* val)
: formatter(f), value(val) {}
unsigned index;
string_view_metadata name;
} val;
};
void on_string() { formatter.write(value); }
void on_pointer() { formatter.write_pointer(value); }
};
// Format specifiers with width and precision resolved at formatting rather
// than parsing time to allow re-using the same parsed specifiers with
// different sets of arguments (precompilation of format strings).
template <typename Char>
struct dynamic_format_specs : basic_format_specs<Char> {
arg_ref<Char> width_ref;
arg_ref<Char> precision_ref;
iterator operator()(const char_type* value) {
if (!specs_) return write(value), out();
internal::handle_cstring_type_spec(specs_->type,
cstring_spec_handler(*this, value));
return out();
}
iterator operator()(basic_string_view<char_type> value) {
if (specs_) {
internal::check_string_type_spec(specs_->type, internal::error_handler());
writer_.write(value, *specs_);
} else {
writer_.write(value);
}
return out();
}
iterator operator()(const void* value) {
if (specs_)
check_pointer_type_spec(specs_->type, internal::error_handler());
write_pointer(value);
return out();
}
};
// Format spec handler that saves references to arguments representing dynamic
// width and precision to be resolved at formatting time.
template <typename ParseContext>
class dynamic_specs_handler
: public specs_setter<typename ParseContext::char_type> {
public:
typedef typename ParseContext::char_type char_type;
template <typename Char> FMT_CONSTEXPR bool is_name_start(Char c) {
return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || '_' == c;
}
FMT_CONSTEXPR dynamic_specs_handler(dynamic_format_specs<char_type>& specs,
ParseContext& ctx)
: specs_setter<char_type>(specs), specs_(specs), context_(ctx) {}
// Parses the range [begin, end) as an unsigned integer. This function assumes
// that the range is non-empty and the first character is a digit.
template <typename Char, typename ErrorHandler>
FMT_CONSTEXPR unsigned parse_nonnegative_int(const Char*& begin,
const Char* end,
ErrorHandler&& eh) {
assert(begin != end && '0' <= *begin && *begin <= '9');
if (*begin == '0') {
++begin;
return 0;
}
unsigned value = 0;
// Convert to unsigned to prevent a warning.
unsigned max_int = (std::numeric_limits<int>::max)();
unsigned big = max_int / 10;
do {
// Check for overflow.
if (value > big) {
value = max_int + 1;
break;
}
value = value * 10 + unsigned(*begin - '0');
++begin;
} while (begin != end && '0' <= *begin && *begin <= '9');
if (value > max_int) eh.on_error("number is too big");
return value;
}
FMT_CONSTEXPR dynamic_specs_handler(const dynamic_specs_handler& other)
: specs_setter<char_type>(other),
specs_(other.specs_),
context_(other.context_) {}
template <typename Context> class custom_formatter {
private:
typedef typename Context::char_type char_type;
template <typename Id> FMT_CONSTEXPR void on_dynamic_width(Id arg_id) {
specs_.width_ref = make_arg_ref(arg_id);
basic_parse_context<char_type>& parse_ctx_;
Context& ctx_;
public:
explicit custom_formatter(basic_parse_context<char_type>& parse_ctx,
Context& ctx)
: parse_ctx_(parse_ctx), ctx_(ctx) {}
bool operator()(typename basic_format_arg<Context>::handle h) const {
h.format(parse_ctx_, ctx_);
return true;
}
template <typename Id> FMT_CONSTEXPR void on_dynamic_precision(Id arg_id) {
specs_.precision_ref = make_arg_ref(arg_id);
template <typename T> bool operator()(T) const { return false; }
};
template <typename T> struct is_integer {
enum {
value = std::is_integral<T>::value && !std::is_same<T, bool>::value &&
!std::is_same<T, char>::value && !std::is_same<T, wchar_t>::value
};
};
template <typename ErrorHandler> class width_checker {
public:
explicit FMT_CONSTEXPR width_checker(ErrorHandler& eh) : handler_(eh) {}
template <typename T, FMT_ENABLE_IF(is_integer<T>::value)>
FMT_CONSTEXPR unsigned long long operator()(T value) {
if (is_negative(value)) handler_.on_error("negative width");
return static_cast<unsigned long long>(value);
}
FMT_CONSTEXPR void on_error(const char* message) {
context_.on_error(message);
template <typename T, FMT_ENABLE_IF(!is_integer<T>::value)>
FMT_CONSTEXPR unsigned long long operator()(T) {
handler_.on_error("width is not integer");
return 0;
}
private:
typedef arg_ref<char_type> arg_ref_type;
ErrorHandler& handler_;
};
FMT_CONSTEXPR arg_ref_type make_arg_ref(unsigned arg_id) {
context_.check_arg_id(arg_id);
return arg_ref_type(arg_id);
}
template <typename ErrorHandler> class precision_checker {
public:
explicit FMT_CONSTEXPR precision_checker(ErrorHandler& eh) : handler_(eh) {}
FMT_CONSTEXPR arg_ref_type make_arg_ref(auto_id) {
return arg_ref_type(context_.next_arg_id());
template <typename T, FMT_ENABLE_IF(is_integer<T>::value)>
FMT_CONSTEXPR unsigned long long operator()(T value) {
if (is_negative(value)) handler_.on_error("negative precision");
return static_cast<unsigned long long>(value);
}
FMT_CONSTEXPR arg_ref_type make_arg_ref(basic_string_view<char_type> arg_id) {
context_.check_arg_id(arg_id);
basic_string_view<char_type> format_str(context_.begin(),
context_.end() - context_.begin());
const auto id_metadata = string_view_metadata(format_str, arg_id);
return arg_ref_type(id_metadata);
template <typename T, FMT_ENABLE_IF(!is_integer<T>::value)>
FMT_CONSTEXPR unsigned long long operator()(T) {
handler_.on_error("precision is not integer");
return 0;
}
dynamic_format_specs<char_type>& specs_;
ParseContext& context_;
private:
ErrorHandler& handler_;
};
template <typename Char, typename IDHandler>
FMT_CONSTEXPR const Char* parse_arg_id(const Char* begin, const Char* end,
IDHandler&& handler) {
assert(begin != end);
Char c = *begin;
if (c == '}' || c == ':') return handler(), begin;
if (c >= '0' && c <= '9') {
unsigned index = parse_nonnegative_int(begin, end, handler);
if (begin == end || (*begin != '}' && *begin != ':'))
return handler.on_error("invalid format string"), begin;
handler(index);
return begin;
}
if (!is_name_start(c))
return handler.on_error("invalid format string"), begin;
auto it = begin;
do {
++it;
} while (it != end && (is_name_start(c = *it) || ('0' <= c && c <= '9')));
handler(basic_string_view<Char>(begin, to_unsigned(it - begin)));
return it;
}
// A format specifier handler that sets fields in basic_format_specs.
template <typename Char> class specs_setter {
public:
explicit FMT_CONSTEXPR specs_setter(basic_format_specs<Char>& specs)
: specs_(specs) {}
// Adapts SpecHandler to IDHandler API for dynamic width.
template <typename SpecHandler, typename Char> struct width_adapter {
explicit FMT_CONSTEXPR width_adapter(SpecHandler& h) : handler(h) {}
FMT_CONSTEXPR specs_setter(const specs_setter& other)
: specs_(other.specs_) {}
FMT_CONSTEXPR void operator()() { handler.on_dynamic_width(auto_id()); }
FMT_CONSTEXPR void operator()(unsigned id) { handler.on_dynamic_width(id); }
FMT_CONSTEXPR void operator()(basic_string_view<Char> id) {
handler.on_dynamic_width(id);
FMT_CONSTEXPR void on_align(alignment align) { specs_.align_ = align; }
FMT_CONSTEXPR void on_fill(Char fill) { specs_.fill_ = fill; }
FMT_CONSTEXPR void on_plus() { specs_.flags |= SIGN_FLAG | PLUS_FLAG; }
FMT_CONSTEXPR void on_minus() { specs_.flags |= MINUS_FLAG; }
FMT_CONSTEXPR void on_space() { specs_.flags |= SIGN_FLAG; }
FMT_CONSTEXPR void on_hash() { specs_.flags |= HASH_FLAG; }
FMT_CONSTEXPR void on_zero() {
specs_.align_ = ALIGN_NUMERIC;
specs_.fill_ = '0';
}
FMT_CONSTEXPR void on_error(const char* message) {
handler.on_error(message);
FMT_CONSTEXPR void on_width(unsigned width) { specs_.width_ = width; }
FMT_CONSTEXPR void on_precision(unsigned precision) {
specs_.precision = static_cast<int>(precision);
}
FMT_CONSTEXPR void end_precision() {}
SpecHandler& handler;
FMT_CONSTEXPR void on_type(Char type) {
specs_.type = static_cast<char>(type);
}
protected:
basic_format_specs<Char>& specs_;
};
// Adapts SpecHandler to IDHandler API for dynamic precision.
template <typename SpecHandler, typename Char> struct precision_adapter {
explicit FMT_CONSTEXPR precision_adapter(SpecHandler& h) : handler(h) {}
template <typename ErrorHandler> class numeric_specs_checker {
public:
FMT_CONSTEXPR numeric_specs_checker(ErrorHandler& eh, internal::type arg_type)
: error_handler_(eh), arg_type_(arg_type) {}
FMT_CONSTEXPR void operator()() { handler.on_dynamic_precision(auto_id()); }
FMT_CONSTEXPR void operator()(unsigned id) {
handler.on_dynamic_precision(id);
FMT_CONSTEXPR void require_numeric_argument() {
if (!is_arithmetic(arg_type_))
error_handler_.on_error("format specifier requires numeric argument");
}
FMT_CONSTEXPR void operator()(basic_string_view<Char> id) {
handler.on_dynamic_precision(id);
FMT_CONSTEXPR void check_sign() {
require_numeric_argument();
if (is_integral(arg_type_) && arg_type_ != int_type &&
arg_type_ != long_long_type && arg_type_ != internal::char_type) {
error_handler_.on_error("format specifier requires signed argument");
}
}
FMT_CONSTEXPR void on_error(const char* message) {
handler.on_error(message);
FMT_CONSTEXPR void check_precision() {
if (is_integral(arg_type_) || arg_type_ == internal::pointer_type)
error_handler_.on_error("precision not allowed for this argument type");
}
SpecHandler& handler;
private:
ErrorHandler& error_handler_;
internal::type arg_type_;
};
// Parses fill and alignment.
template <typename Char, typename Handler>
FMT_CONSTEXPR const Char* parse_align(const Char* begin, const Char* end,
Handler&& handler) {
FMT_ASSERT(begin != end, "");
alignment align = ALIGN_DEFAULT;
int i = 0;
if (begin + 1 != end) ++i;
do {
switch (static_cast<char>(begin[i])) {
case '<':
align = ALIGN_LEFT;
break;
case '>':
align = ALIGN_RIGHT;
break;
case '=':
align = ALIGN_NUMERIC;
break;
case '^':
align = ALIGN_CENTER;
break;
}
if (align != ALIGN_DEFAULT) {
if (i > 0) {
auto c = *begin;
if (c == '{')
return handler.on_error("invalid fill character '{'"), begin;
begin += 2;
handler.on_fill(c);
} else
++begin;
handler.on_align(align);
break;
}
} while (i-- > 0);
return begin;
}
// A format specifier handler that checks if specifiers are consistent with the
// argument type.
template <typename Handler> class specs_checker : public Handler {
public:
FMT_CONSTEXPR specs_checker(const Handler& handler, internal::type arg_type)
: Handler(handler), checker_(*this, arg_type) {}
template <typename Char, typename Handler>
FMT_CONSTEXPR const Char* parse_width(const Char* begin, const Char* end,
Handler&& handler) {
FMT_ASSERT(begin != end, "");
if ('0' <= *begin && *begin <= '9') {
handler.on_width(parse_nonnegative_int(begin, end, handler));
} else if (*begin == '{') {
++begin;
if (begin != end)
begin = parse_arg_id(begin, end, width_adapter<Handler, Char>(handler));
if (begin == end || *begin != '}')
return handler.on_error("invalid format string"), begin;
++begin;
}
return begin;
}
FMT_CONSTEXPR specs_checker(const specs_checker& other)
: Handler(other), checker_(*this, other.arg_type_) {}
template <typename Char, typename Handler>
FMT_CONSTEXPR const Char* parse_precision(const Char* begin, const Char* end,
Handler&& handler) {
++begin;
auto c = begin != end ? *begin : Char();
if ('0' <= c && c <= '9') {
handler.on_precision(parse_nonnegative_int(begin, end, handler));
} else if (c == '{') {
++begin;
if (begin != end) {
begin =
parse_arg_id(begin, end, precision_adapter<Handler, Char>(handler));
}
if (begin == end || *begin++ != '}')
return handler.on_error("invalid format string"), begin;
} else {
return handler.on_error("missing precision specifier"), begin;
FMT_CONSTEXPR void on_align(alignment align) {
if (align == ALIGN_NUMERIC) checker_.require_numeric_argument();
Handler::on_align(align);
}
handler.end_precision();
return begin;
}
// Parses standard format specifiers and sends notifications about parsed
// components to handler.
template <typename Char, typename SpecHandler>
FMT_CONSTEXPR const Char* parse_format_specs(const Char* begin, const Char* end,
SpecHandler&& handler) {
if (begin == end || *begin == '}') return begin;
FMT_CONSTEXPR void on_plus() {
checker_.check_sign();
Handler::on_plus();
}
begin = parse_align(begin, end, handler);
if (begin == end) return begin;
FMT_CONSTEXPR void on_minus() {
checker_.check_sign();
Handler::on_minus();
}
// Parse sign.
switch (static_cast<char>(*begin)) {
case '+':
handler.on_plus();
++begin;
break;
case '-':
handler.on_minus();
++begin;
break;
case ' ':
handler.on_space();
++begin;
break;
FMT_CONSTEXPR void on_space() {
checker_.check_sign();
Handler::on_space();
}
if (begin == end) return begin;
if (*begin == '#') {
handler.on_hash();
if (++begin == end) return begin;
FMT_CONSTEXPR void on_hash() {
checker_.require_numeric_argument();
Handler::on_hash();
}
// Parse zero flag.
if (*begin == '0') {
handler.on_zero();
if (++begin == end) return begin;
FMT_CONSTEXPR void on_zero() {
checker_.require_numeric_argument();
Handler::on_zero();
}
begin = parse_width(begin, end, handler);
if (begin == end) return begin;
FMT_CONSTEXPR void end_precision() { checker_.check_precision(); }
// Parse precision.
if (*begin == '.') {
begin = parse_precision(begin, end, handler);
}
private:
numeric_specs_checker<Handler> checker_;
};
// Parse type.
if (begin != end && *begin != '}') handler.on_type(*begin++);
return begin;
template <template <typename> class Handler, typename T, typename FormatArg,
typename ErrorHandler>
FMT_CONSTEXPR void set_dynamic_spec(T& value, FormatArg arg, ErrorHandler eh) {
unsigned long long big_value =
visit_format_arg(Handler<ErrorHandler>(eh), arg);
if (big_value > to_unsigned((std::numeric_limits<int>::max)()))
eh.on_error("number is too big");
value = static_cast<T>(big_value);
}
// Return the result via the out param to workaround gcc bug 77539.
template <bool IS_CONSTEXPR, typename T, typename Ptr = const T*>
FMT_CONSTEXPR bool find(Ptr first, Ptr last, T value, Ptr& out) {
for (out = first; out != last; ++out) {
if (*out == value) return true;
}
return false;
}
struct auto_id {};
template <>
inline bool find<false, char>(const char* first, const char* last, char value,
const char*& out) {
out = static_cast<const char*>(
std::memchr(first, value, internal::to_unsigned(last - first)));
return out != nullptr;
template <typename Context>
FMT_CONSTEXPR typename Context::format_arg get_arg(Context& ctx, unsigned id) {
auto arg = ctx.arg(id);
if (!arg) ctx.on_error("argument index out of range");
return arg;
}
template <typename Handler, typename Char> struct id_adapter {
FMT_CONSTEXPR void operator()() { handler.on_arg_id(); }
FMT_CONSTEXPR void operator()(unsigned id) { handler.on_arg_id(id); }
FMT_CONSTEXPR void operator()(basic_string_view<Char> id) {
handler.on_arg_id(id);
// The standard format specifier handler with checking.
template <typename ParseContext, typename Context>
class specs_handler : public specs_setter<typename Context::char_type> {
public:
typedef typename Context::char_type char_type;
FMT_CONSTEXPR specs_handler(basic_format_specs<char_type>& specs,
ParseContext& parse_ctx, Context& ctx)
: specs_setter<char_type>(specs),
parse_context_(parse_ctx),
context_(ctx) {}
template <typename Id> FMT_CONSTEXPR void on_dynamic_width(Id arg_id) {
set_dynamic_spec<width_checker>(this->specs_.width_, get_arg(arg_id),
context_.error_handler());
}
FMT_CONSTEXPR void on_error(const char* message) {
handler.on_error(message);
template <typename Id> FMT_CONSTEXPR void on_dynamic_precision(Id arg_id) {
set_dynamic_spec<precision_checker>(this->specs_.precision, get_arg(arg_id),
context_.error_handler());
}
Handler& handler;
};
template <bool IS_CONSTEXPR, typename Char, typename Handler>
FMT_CONSTEXPR void parse_format_string(basic_string_view<Char> format_str,
Handler&& handler) {
struct writer {
FMT_CONSTEXPR void operator()(const Char* begin, const Char* end) {
if (begin == end) return;
for (;;) {
const Char* p = nullptr;
if (!find<IS_CONSTEXPR>(begin, end, '}', p))
return handler_.on_text(begin, end);
++p;
if (p == end || *p != '}')
return handler_.on_error("unmatched '}' in format string");
handler_.on_text(begin, p);
begin = p + 1;
}
}
Handler& handler_;
} write{handler};
auto begin = format_str.data();
auto end = begin + format_str.size();
while (begin != end) {
// Doing two passes with memchr (one for '{' and another for '}') is up to
// 2.5x faster than the naive one-pass implementation on big format strings.
const Char* p = begin;
if (*begin != '{' && !find<IS_CONSTEXPR>(begin, end, '{', p))
return write(begin, end);
write(begin, p);
++p;
if (p == end) return handler.on_error("invalid format string");
if (static_cast<char>(*p) == '}') {
handler.on_arg_id();
handler.on_replacement_field(p);
} else if (*p == '{') {
handler.on_text(p, p + 1);
} else {
p = parse_arg_id(p, end, id_adapter<Handler, Char>{handler});
Char c = p != end ? *p : Char();
if (c == '}') {
handler.on_replacement_field(p);
} else if (c == ':') {
p = handler.on_format_specs(p + 1, end);
if (p == end || *p != '}')
return handler.on_error("unknown format specifier");
} else {
return handler.on_error("missing '}' in format string");
}
}
begin = p + 1;
void on_error(const char* message) { context_.on_error(message); }
private:
// This is only needed for compatibility with gcc 4.4.
typedef typename Context::format_arg format_arg;
FMT_CONSTEXPR format_arg get_arg(auto_id) {
return internal::get_arg(context_, parse_context_.next_arg_id());
}
}
template <typename T, typename ParseContext>
FMT_CONSTEXPR const typename ParseContext::char_type* parse_format_specs(
ParseContext& ctx) {
using char_type = typename ParseContext::char_type;
using context = buffer_context<char_type>;
using mapped_type =
conditional_t<internal::mapped_type_constant<T, context>::value !=
internal::custom_type,
decltype(arg_mapper<context>().map(std::declval<T>())), T>;
conditional_t<has_formatter<mapped_type, context>::value,
formatter<mapped_type, char_type>,
internal::fallback_formatter<T, char_type>>
f;
return f.parse(ctx);
}
FMT_CONSTEXPR format_arg get_arg(unsigned arg_id) {
parse_context_.check_arg_id(arg_id);
return internal::get_arg(context_, arg_id);
}
template <typename Char, typename ErrorHandler, typename... Args>
class format_string_checker {
public:
explicit FMT_CONSTEXPR format_string_checker(
basic_string_view<Char> format_str, ErrorHandler eh)
: arg_id_((std::numeric_limits<unsigned>::max)()),
context_(format_str, eh),
parse_funcs_{&parse_format_specs<Args, parse_context_type>...} {}
FMT_CONSTEXPR format_arg get_arg(basic_string_view<char_type> arg_id) {
parse_context_.check_arg_id(arg_id);
return context_.arg(arg_id);
}
FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
ParseContext& parse_context_;
Context& context_;
};
FMT_CONSTEXPR void on_arg_id() {
arg_id_ = context_.next_arg_id();
check_arg_id();
}
FMT_CONSTEXPR void on_arg_id(unsigned id) {
arg_id_ = id;
context_.check_arg_id(id);
check_arg_id();
struct string_view_metadata {
FMT_CONSTEXPR string_view_metadata() : offset_(0u), size_(0u) {}
template <typename Char>
FMT_CONSTEXPR string_view_metadata(basic_string_view<Char> primary_string,
basic_string_view<Char> view)
: offset_(view.data() - primary_string.data()), size_(view.size()) {}
FMT_CONSTEXPR string_view_metadata(std::size_t offset, std::size_t size)
: offset_(offset), size_(size) {}
template <typename Char>
FMT_CONSTEXPR basic_string_view<Char> to_view(const Char* str) const {
return {str + offset_, size_};
}
FMT_CONSTEXPR void on_arg_id(basic_string_view<Char>) {
on_error("compile-time checks don't support named arguments");
std::size_t offset_;
std::size_t size_;
};
// An argument reference.
template <typename Char> struct arg_ref {
enum Kind { NONE, INDEX, NAME };
typedef Char char_type;
FMT_CONSTEXPR arg_ref() : kind(NONE), val() {}
FMT_CONSTEXPR explicit arg_ref(unsigned index) : kind(INDEX), val(index) {}
FMT_CONSTEXPR explicit arg_ref(string_view_metadata name)
: kind(NAME), val(name) {}
FMT_CONSTEXPR arg_ref& operator=(unsigned idx) {
kind = INDEX;
val.index = idx;
return *this;
}
FMT_CONSTEXPR void on_replacement_field(const Char*) {}
Kind kind;
union value {
FMT_CONSTEXPR value() : index(0u) {}
FMT_CONSTEXPR value(unsigned id) : index(id) {}
FMT_CONSTEXPR value(string_view_metadata n) : name(n) {}
FMT_CONSTEXPR const Char* on_format_specs(const Char* begin, const Char*) {
advance_to(context_, begin);
return arg_id_ < NUM_ARGS ? parse_funcs_[arg_id_](context_) : begin;
unsigned index;
string_view_metadata name;
} val;
};
// Format specifiers with width and precision resolved at formatting rather
// than parsing time to allow re-using the same parsed specifiers with
// different sets of arguments (precompilation of format strings).
template <typename Char>
struct dynamic_format_specs : basic_format_specs<Char> {
arg_ref<Char> width_ref;
arg_ref<Char> precision_ref;
};
// Format spec handler that saves references to arguments representing dynamic
// width and precision to be resolved at formatting time.
template <typename ParseContext>
class dynamic_specs_handler
: public specs_setter<typename ParseContext::char_type> {
public:
typedef typename ParseContext::char_type char_type;
FMT_CONSTEXPR dynamic_specs_handler(dynamic_format_specs<char_type>& specs,
ParseContext& ctx)
: specs_setter<char_type>(specs), specs_(specs), context_(ctx) {}
FMT_CONSTEXPR dynamic_specs_handler(const dynamic_specs_handler& other)
: specs_setter<char_type>(other),
specs_(other.specs_),
context_(other.context_) {}
template <typename Id> FMT_CONSTEXPR void on_dynamic_width(Id arg_id) {
specs_.width_ref = make_arg_ref(arg_id);
}
template <typename Id> FMT_CONSTEXPR void on_dynamic_precision(Id arg_id) {
specs_.precision_ref = make_arg_ref(arg_id);
}
FMT_CONSTEXPR void on_error(const char* message) {
......@@ -2108,586 +2179,513 @@ class format_string_checker {
}
private:
typedef basic_parse_context<Char, ErrorHandler> parse_context_type;
enum { NUM_ARGS = sizeof...(Args) };
typedef arg_ref<char_type> arg_ref_type;
FMT_CONSTEXPR void check_arg_id() {
if (arg_id_ >= NUM_ARGS) context_.on_error("argument index out of range");
FMT_CONSTEXPR arg_ref_type make_arg_ref(unsigned arg_id) {
context_.check_arg_id(arg_id);
return arg_ref_type(arg_id);
}
// Format specifier parsing function.
typedef const Char* (*parse_func)(parse_context_type&);
FMT_CONSTEXPR arg_ref_type make_arg_ref(auto_id) {
return arg_ref_type(context_.next_arg_id());
}
unsigned arg_id_;
parse_context_type context_;
parse_func parse_funcs_[NUM_ARGS > 0 ? NUM_ARGS : 1];
FMT_CONSTEXPR arg_ref_type make_arg_ref(basic_string_view<char_type> arg_id) {
context_.check_arg_id(arg_id);
basic_string_view<char_type> format_str(context_.begin(),
context_.end() - context_.begin());
const auto id_metadata = string_view_metadata(format_str, arg_id);
return arg_ref_type(id_metadata);
}
dynamic_format_specs<char_type>& specs_;
ParseContext& context_;
};
template <typename Char, typename ErrorHandler, typename... Args>
FMT_CONSTEXPR bool do_check_format_string(basic_string_view<Char> s,
ErrorHandler eh = ErrorHandler()) {
format_string_checker<Char, ErrorHandler, Args...> checker(s, eh);
parse_format_string<true>(s, checker);
return true;
template <typename Char, typename IDHandler>
FMT_CONSTEXPR const Char* parse_arg_id(const Char* begin, const Char* end,
IDHandler&& handler) {
assert(begin != end);
Char c = *begin;
if (c == '}' || c == ':') return handler(), begin;
if (c >= '0' && c <= '9') {
unsigned index = parse_nonnegative_int(begin, end, handler);
if (begin == end || (*begin != '}' && *begin != ':'))
return handler.on_error("invalid format string"), begin;
handler(index);
return begin;
}
if (!is_name_start(c))
return handler.on_error("invalid format string"), begin;
auto it = begin;
do {
++it;
} while (it != end && (is_name_start(c = *it) || ('0' <= c && c <= '9')));
handler(basic_string_view<Char>(begin, to_unsigned(it - begin)));
return it;
}
template <typename... Args, typename S,
enable_if_t<(is_compile_string<S>::value), int>>
void check_format_string(S format_str) {
typedef typename S::char_type char_t;
FMT_CONSTEXPR_DECL bool invalid_format =
internal::do_check_format_string<char_t, internal::error_handler,
Args...>(to_string_view(format_str));
(void)invalid_format;
}
// Adapts SpecHandler to IDHandler API for dynamic width.
template <typename SpecHandler, typename Char> struct width_adapter {
explicit FMT_CONSTEXPR width_adapter(SpecHandler& h) : handler(h) {}
template <template <typename> class Handler, typename Spec, typename Context>
void handle_dynamic_spec(Spec& value, arg_ref<typename Context::char_type> ref,
Context& ctx,
const typename Context::char_type* format_str) {
typedef typename Context::char_type char_type;
switch (ref.kind) {
case arg_ref<char_type>::NONE:
break;
case arg_ref<char_type>::INDEX:
internal::set_dynamic_spec<Handler>(value, ctx.arg(ref.val.index),
ctx.error_handler());
break;
case arg_ref<char_type>::NAME: {
const auto arg_id = ref.val.name.to_view(format_str);
internal::set_dynamic_spec<Handler>(value, ctx.arg(arg_id),
ctx.error_handler());
} break;
FMT_CONSTEXPR void operator()() { handler.on_dynamic_width(auto_id()); }
FMT_CONSTEXPR void operator()(unsigned id) { handler.on_dynamic_width(id); }
FMT_CONSTEXPR void operator()(basic_string_view<Char> id) {
handler.on_dynamic_width(id);
}
}
} // namespace internal
/** The default argument formatter. */
template <typename Range>
class arg_formatter : public internal::arg_formatter_base<Range> {
private:
typedef typename Range::value_type char_type;
typedef internal::arg_formatter_base<Range> base;
typedef basic_format_context<typename base::iterator, char_type> context_type;
FMT_CONSTEXPR void on_error(const char* message) {
handler.on_error(message);
}
context_type& ctx_;
basic_parse_context<char_type>* parse_ctx_;
SpecHandler& handler;
};
public:
typedef Range range;
typedef typename base::iterator iterator;
typedef typename base::format_specs format_specs;
// Adapts SpecHandler to IDHandler API for dynamic precision.
template <typename SpecHandler, typename Char> struct precision_adapter {
explicit FMT_CONSTEXPR precision_adapter(SpecHandler& h) : handler(h) {}
/**
\rst
Constructs an argument formatter object.
*ctx* is a reference to the formatting context,
*spec* contains format specifier information for standard argument types.
\endrst
*/
explicit arg_formatter(context_type& ctx,
basic_parse_context<char_type>* parse_ctx = nullptr,
format_specs* spec = nullptr)
: base(Range(ctx.out()), spec, ctx.locale()),
ctx_(ctx),
parse_ctx_(parse_ctx) {}
FMT_CONSTEXPR void operator()() { handler.on_dynamic_precision(auto_id()); }
FMT_CONSTEXPR void operator()(unsigned id) {
handler.on_dynamic_precision(id);
}
FMT_CONSTEXPR void operator()(basic_string_view<Char> id) {
handler.on_dynamic_precision(id);
}
FMT_DEPRECATED arg_formatter(context_type& ctx, format_specs& spec)
: base(Range(ctx.out()), &spec), ctx_(ctx) {}
FMT_CONSTEXPR void on_error(const char* message) {
handler.on_error(message);
}
using base::operator();
SpecHandler& handler;
};
/** Formats an argument of a user-defined type. */
iterator operator()(typename basic_format_arg<context_type>::handle handle) {
handle.format(*parse_ctx_, ctx_);
return this->out();
}
};
/**
An error returned by an operating system or a language runtime,
for example a file opening error.
*/
class FMT_API system_error : public std::runtime_error {
private:
FMT_API void init(int err_code, string_view format_str, format_args args);
protected:
int error_code_;
system_error() : std::runtime_error("") {}
public:
/**
\rst
Constructs a :class:`fmt::system_error` object with a description
formatted with `fmt::format_system_error`. *message* and additional
arguments passed into the constructor are formatted similarly to
`fmt::format`.
**Example**::
// This throws a system_error with the description
// cannot open file 'madeup': No such file or directory
// or similar (system message may vary).
const char *filename = "madeup";
std::FILE *file = std::fopen(filename, "r");
if (!file)
throw fmt::system_error(errno, "cannot open file '{}'", filename);
\endrst
*/
template <typename... Args>
system_error(int error_code, string_view message, const Args&... args)
: std::runtime_error("") {
init(error_code, message, make_format_args(args...));
}
~system_error() FMT_NOEXCEPT;
int error_code() const { return error_code_; }
};
/**
\rst
Formats an error returned by an operating system or a language runtime,
for example a file opening error, and writes it to *out* in the following
form:
.. parsed-literal::
*<message>*: *<system-message>*
where *<message>* is the passed message and *<system-message>* is
the system message corresponding to the error code.
*error_code* is a system error code as given by ``errno``.
If *error_code* is not a valid error code such as -1, the system message
may look like "Unknown error -1" and is platform-dependent.
\endrst
*/
FMT_API void format_system_error(internal::buffer<char>& out, int error_code,
fmt::string_view message) FMT_NOEXCEPT;
/**
This template provides operations for formatting and writing data into a
character range.
*/
template <typename Range> class basic_writer {
public:
typedef typename Range::value_type char_type;
typedef decltype(std::declval<Range>().begin()) iterator;
typedef basic_format_specs<char_type> format_specs;
private:
iterator out_; // Output iterator.
internal::locale_ref locale_;
// Attempts to reserve space for n extra characters in the output range.
// Returns a pointer to the reserved range or a reference to out_.
auto reserve(std::size_t n) -> decltype(internal::reserve(out_, n)) {
return internal::reserve(out_, n);
}
// Writes a value in the format
// <left-padding><value><right-padding>
// where <value> is written by f(it).
template <typename F> void write_padded(const align_spec& spec, F&& f) {
unsigned width = spec.width(); // User-perceived width (in code points).
size_t size = f.size(); // The number of code units.
size_t num_code_points = width != 0 ? f.width() : size;
if (width <= num_code_points) return f(reserve(size));
auto&& it = reserve(width + (size - num_code_points));
char_type fill = static_cast<char_type>(spec.fill());
std::size_t padding = width - num_code_points;
if (spec.align() == ALIGN_RIGHT) {
it = std::fill_n(it, padding, fill);
f(it);
} else if (spec.align() == ALIGN_CENTER) {
std::size_t left_padding = padding / 2;
it = std::fill_n(it, left_padding, fill);
f(it);
it = std::fill_n(it, padding - left_padding, fill);
} else {
f(it);
it = std::fill_n(it, padding, fill);
}
}
template <typename F> struct padded_int_writer {
size_t size_;
string_view prefix;
char_type fill;
std::size_t padding;
F f;
size_t size() const { return size_; }
size_t width() const { return size_; }
template <typename It> void operator()(It&& it) const {
if (prefix.size() != 0)
it = internal::copy_str<char_type>(prefix.begin(), prefix.end(), it);
it = std::fill_n(it, padding, fill);
f(it);
// Parses fill and alignment.
template <typename Char, typename Handler>
FMT_CONSTEXPR const Char* parse_align(const Char* begin, const Char* end,
Handler&& handler) {
FMT_ASSERT(begin != end, "");
alignment align = ALIGN_DEFAULT;
int i = 0;
if (begin + 1 != end) ++i;
do {
switch (static_cast<char>(begin[i])) {
case '<':
align = ALIGN_LEFT;
break;
case '>':
align = ALIGN_RIGHT;
break;
case '=':
align = ALIGN_NUMERIC;
break;
case '^':
align = ALIGN_CENTER;
break;
}
};
// Writes an integer in the format
// <left-padding><prefix><numeric-padding><digits><right-padding>
// where <digits> are written by f(it).
template <typename Spec, typename F>
void write_int(int num_digits, string_view prefix, const Spec& spec, F f) {
std::size_t size = prefix.size() + internal::to_unsigned(num_digits);
char_type fill = static_cast<char_type>(spec.fill());
std::size_t padding = 0;
if (spec.align() == ALIGN_NUMERIC) {
if (spec.width() > size) {
padding = spec.width() - size;
size = spec.width();
}
} else if (spec.precision > num_digits) {
size = prefix.size() + internal::to_unsigned(spec.precision);
padding = internal::to_unsigned(spec.precision - num_digits);
fill = static_cast<char_type>('0');
if (align != ALIGN_DEFAULT) {
if (i > 0) {
auto c = *begin;
if (c == '{')
return handler.on_error("invalid fill character '{'"), begin;
begin += 2;
handler.on_fill(c);
} else
++begin;
handler.on_align(align);
break;
}
align_spec as = spec;
if (spec.align() == ALIGN_DEFAULT) as.align_ = ALIGN_RIGHT;
write_padded(as, padded_int_writer<F>{size, prefix, fill, padding, f});
}
} while (i-- > 0);
return begin;
}
// Writes a decimal integer.
template <typename Int> void write_decimal(Int value) {
typedef typename internal::int_traits<Int>::main_type main_type;
main_type abs_value = static_cast<main_type>(value);
bool is_negative = internal::is_negative(value);
if (is_negative) abs_value = 0 - abs_value;
int num_digits = internal::count_digits(abs_value);
auto&& it =
reserve((is_negative ? 1 : 0) + static_cast<size_t>(num_digits));
if (is_negative) *it++ = static_cast<char_type>('-');
it = internal::format_decimal<char_type>(it, abs_value, num_digits);
template <typename Char, typename Handler>
FMT_CONSTEXPR const Char* parse_width(const Char* begin, const Char* end,
Handler&& handler) {
FMT_ASSERT(begin != end, "");
if ('0' <= *begin && *begin <= '9') {
handler.on_width(parse_nonnegative_int(begin, end, handler));
} else if (*begin == '{') {
++begin;
if (begin != end)
begin = parse_arg_id(begin, end, width_adapter<Handler, Char>(handler));
if (begin == end || *begin != '}')
return handler.on_error("invalid format string"), begin;
++begin;
}
return begin;
}
// The handle_int_type_spec handler that writes an integer.
template <typename Int, typename Spec> struct int_writer {
typedef typename internal::int_traits<Int>::main_type unsigned_type;
basic_writer<Range>& writer;
const Spec& spec;
unsigned_type abs_value;
char prefix[4];
unsigned prefix_size;
string_view get_prefix() const { return string_view(prefix, prefix_size); }
int_writer(basic_writer<Range>& w, Int value, const Spec& s)
: writer(w),
spec(s),
abs_value(static_cast<unsigned_type>(value)),
prefix_size(0) {
if (internal::is_negative(value)) {
prefix[0] = '-';
++prefix_size;
abs_value = 0 - abs_value;
} else if (spec.has(SIGN_FLAG)) {
prefix[0] = spec.has(PLUS_FLAG) ? '+' : ' ';
++prefix_size;
}
}
struct dec_writer {
unsigned_type abs_value;
int num_digits;
template <typename It> void operator()(It&& it) const {
it = internal::format_decimal<char_type>(it, abs_value, num_digits);
}
};
void on_dec() {
int num_digits = internal::count_digits(abs_value);
writer.write_int(num_digits, get_prefix(), spec,
dec_writer{abs_value, num_digits});
}
struct hex_writer {
int_writer& self;
int num_digits;
template <typename It> void operator()(It&& it) const {
it = internal::format_uint<4, char_type>(it, self.abs_value, num_digits,
self.spec.type != 'x');
}
};
void on_hex() {
if (spec.has(HASH_FLAG)) {
prefix[prefix_size++] = '0';
prefix[prefix_size++] = static_cast<char>(spec.type);
}
int num_digits = internal::count_digits<4>(abs_value);
writer.write_int(num_digits, get_prefix(), spec,
hex_writer{*this, num_digits});
template <typename Char, typename Handler>
FMT_CONSTEXPR const Char* parse_precision(const Char* begin, const Char* end,
Handler&& handler) {
++begin;
auto c = begin != end ? *begin : Char();
if ('0' <= c && c <= '9') {
handler.on_precision(parse_nonnegative_int(begin, end, handler));
} else if (c == '{') {
++begin;
if (begin != end) {
begin =
parse_arg_id(begin, end, precision_adapter<Handler, Char>(handler));
}
if (begin == end || *begin++ != '}')
return handler.on_error("invalid format string"), begin;
} else {
return handler.on_error("missing precision specifier"), begin;
}
handler.end_precision();
return begin;
}
template <int BITS> struct bin_writer {
unsigned_type abs_value;
int num_digits;
template <typename It> void operator()(It&& it) const {
it = internal::format_uint<BITS, char_type>(it, abs_value, num_digits);
}
};
// Parses standard format specifiers and sends notifications about parsed
// components to handler.
template <typename Char, typename SpecHandler>
FMT_CONSTEXPR const Char* parse_format_specs(const Char* begin, const Char* end,
SpecHandler&& handler) {
if (begin == end || *begin == '}') return begin;
void on_bin() {
if (spec.has(HASH_FLAG)) {
prefix[prefix_size++] = '0';
prefix[prefix_size++] = static_cast<char>(spec.type);
}
int num_digits = internal::count_digits<1>(abs_value);
writer.write_int(num_digits, get_prefix(), spec,
bin_writer<1>{abs_value, num_digits});
}
begin = parse_align(begin, end, handler);
if (begin == end) return begin;
void on_oct() {
int num_digits = internal::count_digits<3>(abs_value);
if (spec.has(HASH_FLAG) && spec.precision <= num_digits) {
// Octal prefix '0' is counted as a digit, so only add it if precision
// is not greater than the number of digits.
prefix[prefix_size++] = '0';
}
writer.write_int(num_digits, get_prefix(), spec,
bin_writer<3>{abs_value, num_digits});
}
// Parse sign.
switch (static_cast<char>(*begin)) {
case '+':
handler.on_plus();
++begin;
break;
case '-':
handler.on_minus();
++begin;
break;
case ' ':
handler.on_space();
++begin;
break;
}
if (begin == end) return begin;
enum { SEP_SIZE = 1 };
if (*begin == '#') {
handler.on_hash();
if (++begin == end) return begin;
}
struct num_writer {
unsigned_type abs_value;
int size;
char_type sep;
// Parse zero flag.
if (*begin == '0') {
handler.on_zero();
if (++begin == end) return begin;
}
template <typename It> void operator()(It&& it) const {
basic_string_view<char_type> s(&sep, SEP_SIZE);
it = internal::format_decimal<char_type>(
it, abs_value, size, internal::add_thousands_sep<char_type>(s));
}
};
begin = parse_width(begin, end, handler);
if (begin == end) return begin;
void on_num() {
int num_digits = internal::count_digits(abs_value);
char_type sep = internal::thousands_sep<char_type>(writer.locale_);
int size = num_digits + SEP_SIZE * ((num_digits - 1) / 3);
writer.write_int(size, get_prefix(), spec,
num_writer{abs_value, size, sep});
}
// Parse precision.
if (*begin == '.') {
begin = parse_precision(begin, end, handler);
}
FMT_NORETURN void on_error() {
FMT_THROW(format_error("invalid type specifier"));
}
};
// Parse type.
if (begin != end && *begin != '}') handler.on_type(*begin++);
return begin;
}
// Writes a formatted integer.
template <typename T, typename Spec>
void write_int(T value, const Spec& spec) {
internal::handle_int_type_spec(spec.type,
int_writer<T, Spec>(*this, value, spec));
// Return the result via the out param to workaround gcc bug 77539.
template <bool IS_CONSTEXPR, typename T, typename Ptr = const T*>
FMT_CONSTEXPR bool find(Ptr first, Ptr last, T value, Ptr& out) {
for (out = first; out != last; ++out) {
if (*out == value) return true;
}
return false;
}
enum { INF_SIZE = 3 }; // This is an enum to workaround a bug in MSVC.
template <>
inline bool find<false, char>(const char* first, const char* last, char value,
const char*& out) {
out = static_cast<const char*>(
std::memchr(first, value, internal::to_unsigned(last - first)));
return out != nullptr;
}
struct inf_or_nan_writer {
char sign;
bool as_percentage;
const char* str;
template <typename Handler, typename Char> struct id_adapter {
FMT_CONSTEXPR void operator()() { handler.on_arg_id(); }
FMT_CONSTEXPR void operator()(unsigned id) { handler.on_arg_id(id); }
FMT_CONSTEXPR void operator()(basic_string_view<Char> id) {
handler.on_arg_id(id);
}
FMT_CONSTEXPR void on_error(const char* message) {
handler.on_error(message);
}
Handler& handler;
};
size_t size() const {
return static_cast<std::size_t>(INF_SIZE + (sign ? 1 : 0) +
(as_percentage ? 1 : 0));
template <bool IS_CONSTEXPR, typename Char, typename Handler>
FMT_CONSTEXPR void parse_format_string(basic_string_view<Char> format_str,
Handler&& handler) {
struct writer {
FMT_CONSTEXPR void operator()(const Char* begin, const Char* end) {
if (begin == end) return;
for (;;) {
const Char* p = nullptr;
if (!find<IS_CONSTEXPR>(begin, end, '}', p))
return handler_.on_text(begin, end);
++p;
if (p == end || *p != '}')
return handler_.on_error("unmatched '}' in format string");
handler_.on_text(begin, p);
begin = p + 1;
}
}
size_t width() const { return size(); }
template <typename It> void operator()(It&& it) const {
if (sign) *it++ = static_cast<char_type>(sign);
it = internal::copy_str<char_type>(
str, str + static_cast<std::size_t>(INF_SIZE), it);
if (as_percentage) *it++ = static_cast<char_type>('%');
Handler& handler_;
} write{handler};
auto begin = format_str.data();
auto end = begin + format_str.size();
while (begin != end) {
// Doing two passes with memchr (one for '{' and another for '}') is up to
// 2.5x faster than the naive one-pass implementation on big format strings.
const Char* p = begin;
if (*begin != '{' && !find<IS_CONSTEXPR>(begin, end, '{', p))
return write(begin, end);
write(begin, p);
++p;
if (p == end) return handler.on_error("invalid format string");
if (static_cast<char>(*p) == '}') {
handler.on_arg_id();
handler.on_replacement_field(p);
} else if (*p == '{') {
handler.on_text(p, p + 1);
} else {
p = parse_arg_id(p, end, id_adapter<Handler, Char>{handler});
Char c = p != end ? *p : Char();
if (c == '}') {
handler.on_replacement_field(p);
} else if (c == ':') {
p = handler.on_format_specs(p + 1, end);
if (p == end || *p != '}')
return handler.on_error("unknown format specifier");
} else {
return handler.on_error("missing '}' in format string");
}
}
};
struct double_writer {
char sign;
internal::buffer<char>& buffer;
begin = p + 1;
}
}
size_t size() const { return buffer.size() + (sign ? 1 : 0); }
size_t width() const { return size(); }
template <typename T, typename ParseContext>
FMT_CONSTEXPR const typename ParseContext::char_type* parse_format_specs(
ParseContext& ctx) {
using char_type = typename ParseContext::char_type;
using context = buffer_context<char_type>;
using mapped_type =
conditional_t<internal::mapped_type_constant<T, context>::value !=
internal::custom_type,
decltype(arg_mapper<context>().map(std::declval<T>())), T>;
conditional_t<has_formatter<mapped_type, context>::value,
formatter<mapped_type, char_type>,
internal::fallback_formatter<T, char_type>>
f;
return f.parse(ctx);
}
template <typename It> void operator()(It&& it) {
if (sign) *it++ = static_cast<char_type>(sign);
it = internal::copy_str<char_type>(buffer.begin(), buffer.end(), it);
}
};
template <typename Char, typename ErrorHandler, typename... Args>
class format_string_checker {
public:
explicit FMT_CONSTEXPR format_string_checker(
basic_string_view<Char> format_str, ErrorHandler eh)
: arg_id_((std::numeric_limits<unsigned>::max)()),
context_(format_str, eh),
parse_funcs_{&parse_format_specs<Args, parse_context_type>...} {}
class grisu_writer {
private:
internal::buffer<char>& digits_;
size_t size_;
char sign_;
int exp_;
internal::gen_digits_params params_;
FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
public:
grisu_writer(char sign, internal::buffer<char>& digits, int exp,
const internal::gen_digits_params& params)
: digits_(digits), sign_(sign), exp_(exp), params_(params) {
int num_digits = static_cast<int>(digits.size());
int full_exp = num_digits + exp - 1;
int precision = params.num_digits > 0 ? params.num_digits : 11;
params_.fixed |= full_exp >= -4 && full_exp < precision;
auto it = internal::grisu_prettify<char>(
digits.data(), num_digits, exp, internal::counting_iterator<char>(),
params_);
size_ = it.count();
}
FMT_CONSTEXPR void on_arg_id() {
arg_id_ = context_.next_arg_id();
check_arg_id();
}
FMT_CONSTEXPR void on_arg_id(unsigned id) {
arg_id_ = id;
context_.check_arg_id(id);
check_arg_id();
}
FMT_CONSTEXPR void on_arg_id(basic_string_view<Char>) {
on_error("compile-time checks don't support named arguments");
}
size_t size() const { return size_ + (sign_ ? 1 : 0); }
size_t width() const { return size(); }
FMT_CONSTEXPR void on_replacement_field(const Char*) {}
template <typename It> void operator()(It&& it) {
if (sign_) *it++ = static_cast<char_type>(sign_);
int num_digits = static_cast<int>(digits_.size());
it = internal::grisu_prettify<char_type>(digits_.data(), num_digits, exp_,
it, params_);
}
};
FMT_CONSTEXPR const Char* on_format_specs(const Char* begin, const Char*) {
advance_to(context_, begin);
return arg_id_ < NUM_ARGS ? parse_funcs_[arg_id_](context_) : begin;
}
// Formats a floating-point number (double or long double).
template <typename T> void write_double(T value, const format_specs& spec);
FMT_CONSTEXPR void on_error(const char* message) {
context_.on_error(message);
}
template <typename Char> struct str_writer {
const Char* s;
size_t size_;
private:
typedef basic_parse_context<Char, ErrorHandler> parse_context_type;
enum { NUM_ARGS = sizeof...(Args) };
size_t size() const { return size_; }
size_t width() const {
return internal::count_code_points(basic_string_view<Char>(s, size_));
}
FMT_CONSTEXPR void check_arg_id() {
if (arg_id_ >= NUM_ARGS) context_.on_error("argument index out of range");
}
template <typename It> void operator()(It&& it) const {
it = internal::copy_str<char_type>(s, s + size_, it);
}
};
// Format specifier parsing function.
typedef const Char* (*parse_func)(parse_context_type&);
template <typename UIntPtr> struct pointer_writer {
UIntPtr value;
int num_digits;
unsigned arg_id_;
parse_context_type context_;
parse_func parse_funcs_[NUM_ARGS > 0 ? NUM_ARGS : 1];
};
size_t size() const { return num_digits + 2; }
size_t width() const { return size(); }
template <typename Char, typename ErrorHandler, typename... Args>
FMT_CONSTEXPR bool do_check_format_string(basic_string_view<Char> s,
ErrorHandler eh = ErrorHandler()) {
format_string_checker<Char, ErrorHandler, Args...> checker(s, eh);
parse_format_string<true>(s, checker);
return true;
}
template <typename It> void operator()(It&& it) const {
*it++ = static_cast<char_type>('0');
*it++ = static_cast<char_type>('x');
it = internal::format_uint<4, char_type>(it, value, num_digits);
}
};
template <typename... Args, typename S,
enable_if_t<(is_compile_string<S>::value), int>>
void check_format_string(S format_str) {
typedef typename S::char_type char_t;
FMT_CONSTEXPR_DECL bool invalid_format =
internal::do_check_format_string<char_t, internal::error_handler,
Args...>(to_string_view(format_str));
(void)invalid_format;
}
template <typename Char, typename ErrorHandler>
friend class internal::arg_formatter_base;
template <template <typename> class Handler, typename Spec, typename Context>
void handle_dynamic_spec(Spec& value, arg_ref<typename Context::char_type> ref,
Context& ctx,
const typename Context::char_type* format_str) {
typedef typename Context::char_type char_type;
switch (ref.kind) {
case arg_ref<char_type>::NONE:
break;
case arg_ref<char_type>::INDEX:
internal::set_dynamic_spec<Handler>(value, ctx.arg(ref.val.index),
ctx.error_handler());
break;
case arg_ref<char_type>::NAME: {
const auto arg_id = ref.val.name.to_view(format_str);
internal::set_dynamic_spec<Handler>(value, ctx.arg(arg_id),
ctx.error_handler());
} break;
}
}
} // namespace internal
public:
/** Constructs a ``basic_writer`` object. */
explicit basic_writer(Range out,
internal::locale_ref loc = internal::locale_ref())
: out_(out.begin()), locale_(loc) {}
template <typename Range>
using basic_writer FMT_DEPRECATED = internal::basic_writer<Range>;
using writer FMT_DEPRECATED = internal::writer;
using wwriter FMT_DEPRECATED =
internal::basic_writer<internal::buffer_range<wchar_t>>;
iterator out() const { return out_; }
/** The default argument formatter. */
template <typename Range>
class arg_formatter : public internal::arg_formatter_base<Range> {
private:
typedef typename Range::value_type char_type;
typedef internal::arg_formatter_base<Range> base;
typedef basic_format_context<typename base::iterator, char_type> context_type;
void write(int value) { write_decimal(value); }
void write(long value) { write_decimal(value); }
void write(long long value) { write_decimal(value); }
context_type& ctx_;
basic_parse_context<char_type>* parse_ctx_;
void write(unsigned value) { write_decimal(value); }
void write(unsigned long value) { write_decimal(value); }
void write(unsigned long long value) { write_decimal(value); }
public:
typedef Range range;
typedef typename base::iterator iterator;
typedef typename base::format_specs format_specs;
/**
\rst
Formats *value* and writes it to the buffer.
Constructs an argument formatter object.
*ctx* is a reference to the formatting context,
*spec* contains format specifier information for standard argument types.
\endrst
*/
template <typename T, typename FormatSpec, typename... FormatSpecs,
FMT_ENABLE_IF(std::is_integral<T>::value)>
void write(T value, FormatSpec spec, FormatSpecs... specs) {
format_specs s(spec, specs...);
s.align_ = ALIGN_RIGHT;
write_int(value, s);
}
explicit arg_formatter(context_type& ctx,
basic_parse_context<char_type>* parse_ctx = nullptr,
format_specs* spec = nullptr)
: base(Range(ctx.out()), spec, ctx.locale()),
ctx_(ctx),
parse_ctx_(parse_ctx) {}
void write(double value, const format_specs& spec = format_specs()) {
write_double(value, spec);
}
FMT_DEPRECATED arg_formatter(context_type& ctx, format_specs& spec)
: base(Range(ctx.out()), &spec), ctx_(ctx) {}
/**
\rst
Formats *value* using the general format for floating-point numbers
(``'g'``) and writes it to the buffer.
\endrst
*/
void write(long double value, const format_specs& spec = format_specs()) {
write_double(value, spec);
}
using base::operator();
/** Writes a character to the buffer. */
void write(char value) {
auto&& it = reserve(1);
*it++ = value;
/** Formats an argument of a user-defined type. */
iterator operator()(typename basic_format_arg<context_type>::handle handle) {
handle.format(*parse_ctx_, ctx_);
return this->out();
}
};
template <typename Char, FMT_ENABLE_IF(std::is_same<Char, char_type>::value)>
void write(Char value) {
auto&& it = reserve(1);
*it++ = value;
}
/**
An error returned by an operating system or a language runtime,
for example a file opening error.
*/
class FMT_API system_error : public std::runtime_error {
private:
FMT_API void init(int err_code, string_view format_str, format_args args);
protected:
int error_code_;
system_error() : std::runtime_error("") {}
public:
/**
\rst
Writes *value* to the buffer.
\endrst
*/
void write(string_view value) {
auto&& it = reserve(value.size());
it = internal::copy_str<char_type>(value.begin(), value.end(), it);
}
void write(wstring_view value) {
static_assert(std::is_same<char_type, wchar_t>::value, "");
auto&& it = reserve(value.size());
it = std::copy(value.begin(), value.end(), it);
}
\rst
Constructs a :class:`fmt::system_error` object with a description
formatted with `fmt::format_system_error`. *message* and additional
arguments passed into the constructor are formatted similarly to
`fmt::format`.
// Writes a formatted string.
template <typename Char>
void write(const Char* s, std::size_t size, const align_spec& spec) {
write_padded(spec, str_writer<Char>{s, size});
}
**Example**::
template <typename Char>
void write(basic_string_view<Char> s,
const format_specs& spec = format_specs()) {
const Char* data = s.data();
std::size_t size = s.size();
if (spec.precision >= 0 && internal::to_unsigned(spec.precision) < size)
size = internal::to_unsigned(spec.precision);
write(data, size, spec);
// This throws a system_error with the description
// cannot open file 'madeup': No such file or directory
// or similar (system message may vary).
const char *filename = "madeup";
std::FILE *file = std::fopen(filename, "r");
if (!file)
throw fmt::system_error(errno, "cannot open file '{}'", filename);
\endrst
*/
template <typename... Args>
system_error(int error_code, string_view message, const Args&... args)
: std::runtime_error("") {
init(error_code, message, make_format_args(args...));
}
~system_error() FMT_NOEXCEPT;
template <typename UIntPtr>
void write_pointer(UIntPtr value, const align_spec* spec) {
int num_digits = internal::count_digits<4>(value);
auto pw = pointer_writer<UIntPtr>{value, num_digits};
if (!spec) return pw(reserve(num_digits + 2));
align_spec as = *spec;
if (as.align() == ALIGN_DEFAULT) as.align_ = ALIGN_RIGHT;
write_padded(as, pw);
}
int error_code() const { return error_code_; }
};
/**
\rst
Formats an error returned by an operating system or a language runtime,
for example a file opening error, and writes it to *out* in the following
form:
.. parsed-literal::
*<message>*: *<system-message>*
where *<message>* is the passed message and *<system-message>* is
the system message corresponding to the error code.
*error_code* is a system error code as given by ``errno``.
If *error_code* is not a valid error code such as -1, the system message
may look like "Unknown error -1" and is platform-dependent.
\endrst
*/
FMT_API void format_system_error(internal::buffer<char>& out, int error_code,
fmt::string_view message) FMT_NOEXCEPT;
struct float_spec_handler {
char type;
bool upper;
......@@ -2726,7 +2724,8 @@ struct float_spec_handler {
template <typename Range>
template <typename T>
void basic_writer<Range>::write_double(T value, const format_specs& spec) {
void internal::basic_writer<Range>::write_double(T value,
const format_specs& spec) {
// Check type.
float_spec_handler handler(static_cast<char>(spec.type));
internal::handle_float_type_spec(handler.type, handler);
......
......@@ -34,7 +34,7 @@
using std::size_t;
using fmt::basic_memory_buffer;
using fmt::basic_writer;
using fmt::internal::basic_writer;
using fmt::format;
using fmt::format_error;
using fmt::memory_buffer;
......@@ -100,7 +100,7 @@ template <typename Char, typename T>
::testing::AssertionResult check_write(const T& value, const char* type) {
fmt::basic_memory_buffer<Char> buffer;
using range = fmt::internal::buffer_range<Char>;
fmt::basic_writer<range> writer(buffer);
basic_writer<range> writer(buffer);
writer.write(value);
std::basic_string<Char> actual = to_string(buffer);
std::basic_string<Char> expected;
......@@ -582,7 +582,7 @@ TEST(StringViewTest, Ctor) {
TEST(WriterTest, Data) {
memory_buffer buf;
fmt::writer w(buf);
fmt::internal::writer w(buf);
w.write(42);
EXPECT_EQ("42", to_string(buf));
}
......@@ -649,13 +649,13 @@ TEST(WriterTest, WriteLongDouble) {
TEST(WriterTest, WriteDoubleAtBufferBoundary) {
memory_buffer buf;
fmt::writer writer(buf);
fmt::internal::writer writer(buf);
for (int i = 0; i < 100; ++i) writer.write(1.23456789);
}
TEST(WriterTest, WriteDoubleWithFilledBuffer) {
memory_buffer buf;
fmt::writer writer(buf);
fmt::internal::writer writer(buf);
// Fill the buffer.
for (int i = 0; i < fmt::inline_buffer_size; ++i) writer.write(' ');
writer.write(1.2);
......@@ -671,14 +671,10 @@ TEST(WriterTest, WriteWideChar) { CHECK_WRITE_WCHAR(L'a'); }
TEST(WriterTest, WriteString) {
CHECK_WRITE_CHAR("abc");
CHECK_WRITE_WCHAR("abc");
// The following line shouldn't compile:
// std::declval<fmt::basic_writer<fmt::buffer>>().write(L"abc");
}
TEST(WriterTest, WriteWideString) {
CHECK_WRITE_WCHAR(L"abc");
// The following line shouldn't compile:
// std::declval<fmt::basic_writer<fmt::wbuffer>>().write("abc");
}
TEST(FormatToTest, FormatWithoutArgs) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment