Commit 0db4b04a authored by gabime's avatar gabime

Bump bundled fmt to version 6.1.0

parent 1aa9ea92
......@@ -16,16 +16,291 @@
#include <locale>
#include <sstream>
// enable safe chrono durations, unless explicitly disabled
FMT_BEGIN_NAMESPACE
// Enable safe chrono durations, unless explicitly disabled.
#ifndef FMT_SAFE_DURATION_CAST
# define FMT_SAFE_DURATION_CAST 1
#endif
#if FMT_SAFE_DURATION_CAST
# include "safe-duration-cast.h"
#endif
FMT_BEGIN_NAMESPACE
// For conversion between std::chrono::durations without undefined
// behaviour or erroneous results.
// This is a stripped down version of duration_cast, for inclusion in fmt.
// See https://github.com/pauldreik/safe_duration_cast
//
// Copyright Paul Dreik 2019
namespace safe_duration_cast {
template <typename To, typename From,
FMT_ENABLE_IF(!std::is_same<From, To>::value &&
std::numeric_limits<From>::is_signed ==
std::numeric_limits<To>::is_signed)>
FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
ec = 0;
using F = std::numeric_limits<From>;
using T = std::numeric_limits<To>;
static_assert(F::is_integer, "From must be integral");
static_assert(T::is_integer, "To must be integral");
// A and B are both signed, or both unsigned.
if (F::digits <= T::digits) {
// From fits in To without any problem.
} else {
// From does not always fit in To, resort to a dynamic check.
if (from < T::min() || from > T::max()) {
// outside range.
ec = 1;
return {};
}
}
return static_cast<To>(from);
}
/**
* converts From to To, without loss. If the dynamic value of from
* can't be converted to To without loss, ec is set.
*/
template <typename To, typename From,
FMT_ENABLE_IF(!std::is_same<From, To>::value &&
std::numeric_limits<From>::is_signed !=
std::numeric_limits<To>::is_signed)>
FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
ec = 0;
using F = std::numeric_limits<From>;
using T = std::numeric_limits<To>;
static_assert(F::is_integer, "From must be integral");
static_assert(T::is_integer, "To must be integral");
if (F::is_signed && !T::is_signed) {
// From may be negative, not allowed!
if (fmt::internal::is_negative(from)) {
ec = 1;
return {};
}
// From is positive. Can it always fit in To?
if (F::digits <= T::digits) {
// yes, From always fits in To.
} else {
// from may not fit in To, we have to do a dynamic check
if (from > static_cast<From>(T::max())) {
ec = 1;
return {};
}
}
}
if (!F::is_signed && T::is_signed) {
// can from be held in To?
if (F::digits < T::digits) {
// yes, From always fits in To.
} else {
// from may not fit in To, we have to do a dynamic check
if (from > static_cast<From>(T::max())) {
// outside range.
ec = 1;
return {};
}
}
}
// reaching here means all is ok for lossless conversion.
return static_cast<To>(from);
} // function
template <typename To, typename From,
FMT_ENABLE_IF(std::is_same<From, To>::value)>
FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
ec = 0;
return from;
} // function
// clang-format off
/**
* converts From to To if possible, otherwise ec is set.
*
* input | output
* ---------------------------------|---------------
* NaN | NaN
* Inf | Inf
* normal, fits in output | converted (possibly lossy)
* normal, does not fit in output | ec is set
* subnormal | best effort
* -Inf | -Inf
*/
// clang-format on
template <typename To, typename From,
FMT_ENABLE_IF(!std::is_same<From, To>::value)>
FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
ec = 0;
using T = std::numeric_limits<To>;
static_assert(std::is_floating_point<From>::value, "From must be floating");
static_assert(std::is_floating_point<To>::value, "To must be floating");
// catch the only happy case
if (std::isfinite(from)) {
if (from >= T::lowest() && from <= T::max()) {
return static_cast<To>(from);
}
// not within range.
ec = 1;
return {};
}
// nan and inf will be preserved
return static_cast<To>(from);
} // function
template <typename To, typename From,
FMT_ENABLE_IF(std::is_same<From, To>::value)>
FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
ec = 0;
static_assert(std::is_floating_point<From>::value, "From must be floating");
return from;
}
/**
* safe duration cast between integral durations
*/
template <typename To, typename FromRep, typename FromPeriod,
FMT_ENABLE_IF(std::is_integral<FromRep>::value),
FMT_ENABLE_IF(std::is_integral<typename To::rep>::value)>
To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
int& ec) {
using From = std::chrono::duration<FromRep, FromPeriod>;
ec = 0;
// the basic idea is that we need to convert from count() in the from type
// to count() in the To type, by multiplying it with this:
struct Factor
: std::ratio_divide<typename From::period, typename To::period> {};
static_assert(Factor::num > 0, "num must be positive");
static_assert(Factor::den > 0, "den must be positive");
// the conversion is like this: multiply from.count() with Factor::num
// /Factor::den and convert it to To::rep, all this without
// overflow/underflow. let's start by finding a suitable type that can hold
// both To, From and Factor::num
using IntermediateRep =
typename std::common_type<typename From::rep, typename To::rep,
decltype(Factor::num)>::type;
// safe conversion to IntermediateRep
IntermediateRep count =
lossless_integral_conversion<IntermediateRep>(from.count(), ec);
if (ec) {
return {};
}
// multiply with Factor::num without overflow or underflow
if (Factor::num != 1) {
const auto max1 = internal::max_value<IntermediateRep>() / Factor::num;
if (count > max1) {
ec = 1;
return {};
}
const auto min1 = std::numeric_limits<IntermediateRep>::min() / Factor::num;
if (count < min1) {
ec = 1;
return {};
}
count *= Factor::num;
}
// this can't go wrong, right? den>0 is checked earlier.
if (Factor::den != 1) {
count /= Factor::den;
}
// convert to the to type, safely
using ToRep = typename To::rep;
const ToRep tocount = lossless_integral_conversion<ToRep>(count, ec);
if (ec) {
return {};
}
return To{tocount};
}
/**
* safe duration_cast between floating point durations
*/
template <typename To, typename FromRep, typename FromPeriod,
FMT_ENABLE_IF(std::is_floating_point<FromRep>::value),
FMT_ENABLE_IF(std::is_floating_point<typename To::rep>::value)>
To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
int& ec) {
using From = std::chrono::duration<FromRep, FromPeriod>;
ec = 0;
if (std::isnan(from.count())) {
// nan in, gives nan out. easy.
return To{std::numeric_limits<typename To::rep>::quiet_NaN()};
}
// maybe we should also check if from is denormal, and decide what to do about
// it.
// +-inf should be preserved.
if (std::isinf(from.count())) {
return To{from.count()};
}
// the basic idea is that we need to convert from count() in the from type
// to count() in the To type, by multiplying it with this:
struct Factor
: std::ratio_divide<typename From::period, typename To::period> {};
static_assert(Factor::num > 0, "num must be positive");
static_assert(Factor::den > 0, "den must be positive");
// the conversion is like this: multiply from.count() with Factor::num
// /Factor::den and convert it to To::rep, all this without
// overflow/underflow. let's start by finding a suitable type that can hold
// both To, From and Factor::num
using IntermediateRep =
typename std::common_type<typename From::rep, typename To::rep,
decltype(Factor::num)>::type;
// force conversion of From::rep -> IntermediateRep to be safe,
// even if it will never happen be narrowing in this context.
IntermediateRep count =
safe_float_conversion<IntermediateRep>(from.count(), ec);
if (ec) {
return {};
}
// multiply with Factor::num without overflow or underflow
if (Factor::num != 1) {
constexpr auto max1 = internal::max_value<IntermediateRep>() /
static_cast<IntermediateRep>(Factor::num);
if (count > max1) {
ec = 1;
return {};
}
constexpr auto min1 = std::numeric_limits<IntermediateRep>::lowest() /
static_cast<IntermediateRep>(Factor::num);
if (count < min1) {
ec = 1;
return {};
}
count *= static_cast<IntermediateRep>(Factor::num);
}
// this can't go wrong, right? den>0 is checked earlier.
if (Factor::den != 1) {
using common_t = typename std::common_type<IntermediateRep, intmax_t>::type;
count /= static_cast<common_t>(Factor::den);
}
// convert to the to type, safely
using ToRep = typename To::rep;
const ToRep tocount = safe_float_conversion<ToRep>(count, ec);
if (ec) {
return {};
}
return To{tocount};
}
} // namespace safe_duration_cast
#endif
// Prevents expansion of a preceding token as a function-style macro.
// Usage: f FMT_NOMACRO()
......@@ -403,7 +678,7 @@ inline bool isfinite(T value) {
return std::isfinite(value);
}
// Convers value to int and checks that it's in the range [0, upper).
// Converts value to int and checks that it's in the range [0, upper).
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
inline int to_nonnegative_int(T value, int upper) {
FMT_ASSERT(value >= 0 && value <= upper, "invalid value");
......@@ -582,8 +857,8 @@ struct chrono_formatter {
void write(Rep value, int width) {
write_sign();
if (isnan(value)) return write_nan();
uint32_or_64_t<int> n = to_unsigned(
to_nonnegative_int(value, (std::numeric_limits<int>::max)()));
uint32_or_64_or_128_t<int> n =
to_unsigned(to_nonnegative_int(value, max_value<int>()));
int num_digits = internal::count_digits(n);
if (width > num_digits) out = std::fill_n(out, width - num_digits, '0');
out = format_decimal<char_type>(out, n, num_digits);
......@@ -728,7 +1003,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
struct spec_handler {
formatter& f;
basic_parse_context<Char>& context;
basic_format_parse_context<Char>& context;
basic_string_view<Char> format_str;
template <typename Id> FMT_CONSTEXPR arg_ref_type make_arg_ref(Id arg_id) {
......@@ -738,8 +1013,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
FMT_CONSTEXPR arg_ref_type make_arg_ref(basic_string_view<Char> arg_id) {
context.check_arg_id(arg_id);
const auto str_val = internal::string_view_metadata(format_str, arg_id);
return arg_ref_type(str_val);
return arg_ref_type(arg_id);
}
FMT_CONSTEXPR arg_ref_type make_arg_ref(internal::auto_id) {
......@@ -750,7 +1024,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
void on_fill(Char fill) { f.specs.fill[0] = fill; }
void on_align(align_t align) { f.specs.align = align; }
void on_width(unsigned width) { f.specs.width = width; }
void on_precision(unsigned precision) { f.precision = precision; }
void on_precision(unsigned _precision) { f.precision = _precision; }
void end_precision() {}
template <typename Id> void on_dynamic_width(Id arg_id) {
......@@ -762,13 +1036,13 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
}
};
using iterator = typename basic_parse_context<Char>::iterator;
using iterator = typename basic_format_parse_context<Char>::iterator;
struct parse_range {
iterator begin;
iterator end;
};
FMT_CONSTEXPR parse_range do_parse(basic_parse_context<Char>& ctx) {
FMT_CONSTEXPR parse_range do_parse(basic_format_parse_context<Char>& ctx) {
auto begin = ctx.begin(), end = ctx.end();
if (begin == end || *begin == '}') return {begin, begin};
spec_handler handler{*this, ctx, format_str};
......@@ -789,7 +1063,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
public:
formatter() : precision(-1) {}
FMT_CONSTEXPR auto parse(basic_parse_context<Char>& ctx)
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
-> decltype(ctx.begin()) {
auto range = do_parse(ctx);
format_str = basic_string_view<Char>(
......@@ -806,10 +1080,10 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
auto out = std::back_inserter(buf);
using range = internal::output_range<decltype(ctx.out()), Char>;
internal::basic_writer<range> w(range(ctx.out()));
internal::handle_dynamic_spec<internal::width_checker>(
specs.width, width_ref, ctx, format_str.begin());
internal::handle_dynamic_spec<internal::width_checker>(specs.width,
width_ref, ctx);
internal::handle_dynamic_spec<internal::precision_checker>(
precision, precision_ref, ctx, format_str.begin());
precision, precision_ref, ctx);
if (begin == end || *begin == '}') {
out = internal::format_chrono_duration_value(out, d.count(), precision);
internal::format_chrono_duration_unit<Period>(out);
......
......@@ -299,15 +299,15 @@ class text_style {
return static_cast<uint8_t>(ems) != 0;
}
FMT_CONSTEXPR internal::color_type get_foreground() const FMT_NOEXCEPT {
assert(has_foreground() && "no foreground specified for this style");
FMT_ASSERT(has_foreground(), "no foreground specified for this style");
return foreground_color;
}
FMT_CONSTEXPR internal::color_type get_background() const FMT_NOEXCEPT {
assert(has_background() && "no background specified for this style");
FMT_ASSERT(has_background(), "no background specified for this style");
return background_color;
}
FMT_CONSTEXPR emphasis get_emphasis() const FMT_NOEXCEPT {
assert(has_emphasis() && "no emphasis specified for this style");
FMT_ASSERT(has_emphasis(), "no emphasis specified for this style");
return ems;
}
......@@ -470,58 +470,41 @@ inline void reset_color(basic_memory_buffer<Char>& buffer) FMT_NOEXCEPT {
}
template <typename Char>
std::basic_string<Char> vformat(const text_style& ts,
basic_string_view<Char> format_str,
basic_format_args<buffer_context<Char> > args) {
basic_memory_buffer<Char> buffer;
void vformat_to(basic_memory_buffer<Char>& buf, const text_style& ts,
basic_string_view<Char> format_str,
basic_format_args<buffer_context<Char>> args) {
bool has_style = false;
if (ts.has_emphasis()) {
has_style = true;
ansi_color_escape<Char> escape = make_emphasis<Char>(ts.get_emphasis());
buffer.append(escape.begin(), escape.end());
auto emphasis = internal::make_emphasis<Char>(ts.get_emphasis());
buf.append(emphasis.begin(), emphasis.end());
}
if (ts.has_foreground()) {
has_style = true;
ansi_color_escape<Char> escape =
make_foreground_color<Char>(ts.get_foreground());
buffer.append(escape.begin(), escape.end());
auto foreground =
internal::make_foreground_color<Char>(ts.get_foreground());
buf.append(foreground.begin(), foreground.end());
}
if (ts.has_background()) {
has_style = true;
ansi_color_escape<Char> escape =
make_background_color<Char>(ts.get_background());
buffer.append(escape.begin(), escape.end());
auto background =
internal::make_background_color<Char>(ts.get_background());
buf.append(background.begin(), background.end());
}
internal::vformat_to(buffer, format_str, args);
vformat_to(buf, format_str, args);
if (has_style) {
reset_color<Char>(buffer);
internal::reset_color<Char>(buf);
}
return fmt::to_string(buffer);
}
} // namespace internal
template <typename S, typename Char = char_t<S> >
template <typename S, typename Char = char_t<S>>
void vprint(std::FILE* f, const text_style& ts, const S& format,
basic_format_args<buffer_context<Char> > args) {
bool has_style = false;
if (ts.has_emphasis()) {
has_style = true;
internal::fputs<Char>(internal::make_emphasis<Char>(ts.get_emphasis()), f);
}
if (ts.has_foreground()) {
has_style = true;
internal::fputs<Char>(
internal::make_foreground_color<Char>(ts.get_foreground()), f);
}
if (ts.has_background()) {
has_style = true;
internal::fputs<Char>(
internal::make_background_color<Char>(ts.get_background()), f);
}
vprint(f, format, args);
if (has_style) {
internal::reset_color<Char>(f);
}
basic_format_args<buffer_context<Char>> args) {
basic_memory_buffer<Char> buf;
internal::vformat_to(buf, ts, to_string_view(format), args);
buf.push_back(Char(0));
internal::fputs(buf.data(), f);
}
/**
......@@ -536,7 +519,7 @@ template <typename S, typename... Args,
void print(std::FILE* f, const text_style& ts, const S& format_str,
const Args&... args) {
internal::check_format_string<Args...>(format_str);
using context = buffer_context<char_t<S> >;
using context = buffer_context<char_t<S>>;
format_arg_store<context, Args...> as{args...};
vprint(f, ts, format_str, basic_format_args<context>(as));
}
......@@ -554,11 +537,13 @@ void print(const text_style& ts, const S& format_str, const Args&... args) {
return print(stdout, ts, format_str, args...);
}
template <typename S, typename Char = char_t<S> >
template <typename S, typename Char = char_t<S>>
inline std::basic_string<Char> vformat(
const text_style& ts, const S& format_str,
basic_format_args<buffer_context<Char> > args) {
return internal::vformat(ts, to_string_view(format_str), args);
basic_format_args<buffer_context<Char>> args) {
basic_memory_buffer<Char> buf;
internal::vformat_to(buf, ts, to_string_view(format_str), args);
return fmt::to_string(buf);
}
/**
......@@ -573,11 +558,11 @@ inline std::basic_string<Char> vformat(
"The answer is {}", 42);
\endrst
*/
template <typename S, typename... Args, typename Char = char_t<S> >
template <typename S, typename... Args, typename Char = char_t<S>>
inline std::basic_string<Char> format(const text_style& ts, const S& format_str,
const Args&... args) {
return internal::vformat(ts, to_string_view(format_str),
{internal::make_args_checked(format_str, args...)});
return vformat(ts, to_string_view(format_str),
{internal::make_args_checked<Args...>(format_str, args...)});
}
FMT_END_NAMESPACE
......
......@@ -14,433 +14,557 @@
FMT_BEGIN_NAMESPACE
namespace internal {
// Part of a compiled format string. It can be either literal text or a
// replacement field.
template <typename Char> struct format_part {
public:
struct named_argument_id {
FMT_CONSTEXPR named_argument_id(internal::string_view_metadata id)
: id(id) {}
internal::string_view_metadata id;
};
struct argument_id {
FMT_CONSTEXPR argument_id() : argument_id(0u) {}
FMT_CONSTEXPR argument_id(unsigned id)
: which(which_arg_id::index), val(id) {}
FMT_CONSTEXPR argument_id(internal::string_view_metadata id)
: which(which_arg_id::named_index), val(id) {}
enum class kind { arg_index, arg_name, text, replacement };
enum class which_arg_id { index, named_index };
which_arg_id which;
union value {
FMT_CONSTEXPR value() : index(0u) {}
FMT_CONSTEXPR value(unsigned id) : index(id) {}
FMT_CONSTEXPR value(internal::string_view_metadata id)
: named_index(id) {}
unsigned index;
internal::string_view_metadata named_index;
} val;
struct replacement {
arg_ref<Char> arg_id;
dynamic_format_specs<Char> specs;
};
struct specification {
FMT_CONSTEXPR specification() : arg_id(0u) {}
FMT_CONSTEXPR specification(unsigned id) : arg_id(id) {}
kind part_kind;
union value {
unsigned arg_index;
basic_string_view<Char> str;
replacement repl;
FMT_CONSTEXPR specification(internal::string_view_metadata id)
: arg_id(id) {}
FMT_CONSTEXPR value(unsigned index = 0) : arg_index(index) {}
FMT_CONSTEXPR value(basic_string_view<Char> s) : str(s) {}
FMT_CONSTEXPR value(replacement r) : repl(r) {}
} val;
// Position past the end of the argument id.
const Char* arg_id_end = nullptr;
argument_id arg_id;
internal::dynamic_format_specs<Char> parsed_specs;
};
FMT_CONSTEXPR format_part(kind k = kind::arg_index, value v = {})
: part_kind(k), val(v) {}
FMT_CONSTEXPR format_part()
: which(kind::argument_id), end_of_argument_id(0u), val(0u) {}
static FMT_CONSTEXPR format_part make_arg_index(unsigned index) {
return format_part(kind::arg_index, index);
}
static FMT_CONSTEXPR format_part make_arg_name(basic_string_view<Char> name) {
return format_part(kind::arg_name, name);
}
static FMT_CONSTEXPR format_part make_text(basic_string_view<Char> text) {
return format_part(kind::text, text);
}
static FMT_CONSTEXPR format_part make_replacement(replacement repl) {
return format_part(kind::replacement, repl);
}
};
FMT_CONSTEXPR format_part(internal::string_view_metadata text)
: which(kind::text), end_of_argument_id(0u), val(text) {}
template <typename Char> struct part_counter {
unsigned num_parts = 0;
FMT_CONSTEXPR format_part(unsigned id)
: which(kind::argument_id), end_of_argument_id(0u), val(id) {}
FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
if (begin != end) ++num_parts;
}
FMT_CONSTEXPR format_part(named_argument_id arg_id)
: which(kind::named_argument_id), end_of_argument_id(0u), val(arg_id) {}
FMT_CONSTEXPR void on_arg_id() { ++num_parts; }
FMT_CONSTEXPR void on_arg_id(unsigned) { ++num_parts; }
FMT_CONSTEXPR void on_arg_id(basic_string_view<Char>) { ++num_parts; }
FMT_CONSTEXPR format_part(specification spec)
: which(kind::specification), end_of_argument_id(0u), val(spec) {}
FMT_CONSTEXPR void on_replacement_field(const Char*) {}
enum class kind { argument_id, named_argument_id, text, specification };
FMT_CONSTEXPR const Char* on_format_specs(const Char* begin,
const Char* end) {
// Find the matching brace.
unsigned brace_counter = 0;
for (; begin != end; ++begin) {
if (*begin == '{') {
++brace_counter;
} else if (*begin == '}') {
if (brace_counter == 0u) break;
--brace_counter;
}
}
return begin;
}
kind which;
std::size_t end_of_argument_id;
union value {
FMT_CONSTEXPR value() : arg_id(0u) {}
FMT_CONSTEXPR value(unsigned id) : arg_id(id) {}
FMT_CONSTEXPR value(named_argument_id named_id)
: named_arg_id(named_id.id) {}
FMT_CONSTEXPR value(internal::string_view_metadata t) : text(t) {}
FMT_CONSTEXPR value(specification s) : spec(s) {}
unsigned arg_id;
internal::string_view_metadata named_arg_id;
internal::string_view_metadata text;
specification spec;
} val;
FMT_CONSTEXPR void on_error(const char*) {}
};
template <typename Char, typename PartsContainer>
class format_preparation_handler : public internal::error_handler {
// Counts the number of parts in a format string.
template <typename Char>
FMT_CONSTEXPR unsigned count_parts(basic_string_view<Char> format_str) {
part_counter<Char> counter;
parse_format_string<true>(format_str, counter);
return counter.num_parts;
}
template <typename Char, typename PartHandler>
class format_string_compiler : public error_handler {
private:
using part = format_part<Char>;
public:
using iterator = typename basic_string_view<Char>::iterator;
PartHandler handler_;
part part_;
basic_string_view<Char> format_str_;
basic_format_parse_context<Char> parse_context_;
FMT_CONSTEXPR format_preparation_handler(basic_string_view<Char> format,
PartsContainer& parts)
: parts_(parts), format_(format), parse_context_(format) {}
public:
FMT_CONSTEXPR format_string_compiler(basic_string_view<Char> format_str,
PartHandler handler)
: handler_(handler),
format_str_(format_str),
parse_context_(format_str) {}
FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
if (begin == end) return;
const auto offset = begin - format_.data();
const auto size = end - begin;
parts_.push_back(part(string_view_metadata(offset, size)));
if (begin != end)
handler_(part::make_text({begin, to_unsigned(end - begin)}));
}
FMT_CONSTEXPR void on_arg_id() {
parts_.push_back(part(parse_context_.next_arg_id()));
part_ = part::make_arg_index(parse_context_.next_arg_id());
}
FMT_CONSTEXPR void on_arg_id(unsigned id) {
parse_context_.check_arg_id(id);
parts_.push_back(part(id));
part_ = part::make_arg_index(id);
}
FMT_CONSTEXPR void on_arg_id(basic_string_view<Char> id) {
const auto view = string_view_metadata(format_, id);
const auto arg_id = typename part::named_argument_id(view);
parts_.push_back(part(arg_id));
part_ = part::make_arg_name(id);
}
FMT_CONSTEXPR void on_replacement_field(const Char* ptr) {
parts_.back().end_of_argument_id = ptr - format_.begin();
part_.arg_id_end = ptr;
handler_(part_);
}
FMT_CONSTEXPR const Char* on_format_specs(const Char* begin,
const Char* end) {
const auto specs_offset = to_unsigned(begin - format_.begin());
using parse_context = basic_parse_context<Char>;
internal::dynamic_format_specs<Char> parsed_specs;
dynamic_specs_handler<parse_context> handler(parsed_specs, parse_context_);
begin = parse_format_specs(begin, end, handler);
if (*begin != '}') on_error("missing '}' in format string");
auto& last_part = parts_.back();
auto specs = last_part.which == part::kind::argument_id
? typename part::specification(last_part.val.arg_id)
: typename part::specification(last_part.val.named_arg_id);
specs.parsed_specs = parsed_specs;
last_part = part(specs);
last_part.end_of_argument_id = specs_offset;
return begin;
auto repl = typename part::replacement();
dynamic_specs_handler<basic_format_parse_context<Char>> handler(
repl.specs, parse_context_);
auto it = parse_format_specs(begin, end, handler);
if (*it != '}') on_error("missing '}' in format string");
repl.arg_id = part_.part_kind == part::kind::arg_index
? arg_ref<Char>(part_.val.arg_index)
: arg_ref<Char>(part_.val.str);
auto part = part::make_replacement(repl);
part.arg_id_end = begin;
handler_(part);
return it;
}
private:
PartsContainer& parts_;
basic_string_view<Char> format_;
basic_parse_context<Char> parse_context_;
};
template <typename Format, typename PreparedPartsProvider, typename... Args>
class prepared_format {
public:
using char_type = char_t<Format>;
using format_part_t = format_part<char_type>;
constexpr prepared_format(Format f)
: format_(std::move(f)), parts_provider_(to_string_view(format_)) {}
prepared_format() = delete;
using context = buffer_context<char_type>;
template <typename Range, typename Context>
auto vformat_to(Range out, basic_format_args<Context> args) const ->
typename Context::iterator {
const auto format_view = internal::to_string_view(format_);
basic_parse_context<char_type> parse_ctx(format_view);
Context ctx(out.begin(), args);
const auto& parts = parts_provider_.parts();
for (auto part_it = parts.begin(); part_it != parts.end(); ++part_it) {
const auto& part = *part_it;
const auto& value = part.val;
switch (part.which) {
case format_part_t::kind::text: {
const auto text = value.text.to_view(format_view.data());
auto output = ctx.out();
auto&& it = internal::reserve(output, text.size());
it = std::copy_n(text.begin(), text.size(), it);
ctx.advance_to(output);
} break;
case format_part_t::kind::argument_id: {
advance_parse_context_to_specification(parse_ctx, part);
format_arg<Range>(parse_ctx, ctx, value.arg_id);
} break;
case format_part_t::kind::named_argument_id: {
advance_parse_context_to_specification(parse_ctx, part);
const auto named_arg_id =
value.named_arg_id.to_view(format_view.data());
format_arg<Range>(parse_ctx, ctx, named_arg_id);
} break;
case format_part_t::kind::specification: {
const auto& arg_id_value = value.spec.arg_id.val;
const auto arg = value.spec.arg_id.which ==
format_part_t::argument_id::which_arg_id::index
? ctx.arg(arg_id_value.index)
: ctx.arg(arg_id_value.named_index.to_view(
to_string_view(format_).data()));
auto specs = value.spec.parsed_specs;
handle_dynamic_spec<internal::width_checker>(
specs.width, specs.width_ref, ctx, format_view.begin());
handle_dynamic_spec<internal::precision_checker>(
specs.precision, specs.precision_ref, ctx, format_view.begin());
check_prepared_specs(specs, arg.type());
advance_parse_context_to_specification(parse_ctx, part);
ctx.advance_to(
visit_format_arg(arg_formatter<Range>(ctx, nullptr, &specs), arg));
} break;
}
}
// Compiles a format string and invokes handler(part) for each parsed part.
template <bool IS_CONSTEXPR, typename Char, typename PartHandler>
FMT_CONSTEXPR void compile_format_string(basic_string_view<Char> format_str,
PartHandler handler) {
parse_format_string<IS_CONSTEXPR>(
format_str,
format_string_compiler<Char, PartHandler>(format_str, handler));
}
return ctx.out();
}
template <typename Range, typename Context, typename Id>
void format_arg(
basic_format_parse_context<typename Range::value_type>& parse_ctx,
Context& ctx, Id arg_id) {
ctx.advance_to(
visit_format_arg(arg_formatter<Range>(ctx, &parse_ctx), ctx.arg(arg_id)));
}
private:
void advance_parse_context_to_specification(
basic_parse_context<char_type>& parse_ctx,
const format_part_t& part) const {
const auto view = to_string_view(format_);
const auto specification_begin = view.data() + part.end_of_argument_id;
advance_to(parse_ctx, specification_begin);
}
// vformat_to is defined in a subnamespace to prevent ADL.
namespace cf {
template <typename Context, typename Range, typename CompiledFormat>
auto vformat_to(Range out, CompiledFormat& cf, basic_format_args<Context> args)
-> typename Context::iterator {
using char_type = typename Context::char_type;
basic_format_parse_context<char_type> parse_ctx(
to_string_view(cf.format_str_));
Context ctx(out.begin(), args);
const auto& parts = cf.parts();
for (auto part_it = std::begin(parts); part_it != std::end(parts);
++part_it) {
const auto& part = *part_it;
const auto& value = part.val;
using format_part_t = format_part<char_type>;
switch (part.part_kind) {
case format_part_t::kind::text: {
const auto text = value.str;
auto output = ctx.out();
auto&& it = reserve(output, text.size());
it = std::copy_n(text.begin(), text.size(), it);
ctx.advance_to(output);
break;
}
template <typename Range, typename Context, typename Id>
void format_arg(basic_parse_context<char_type>& parse_ctx, Context& ctx,
Id arg_id) const {
parse_ctx.check_arg_id(arg_id);
const auto stopped_at =
visit_format_arg(arg_formatter<Range>(ctx), ctx.arg(arg_id));
ctx.advance_to(stopped_at);
case format_part_t::kind::arg_index:
advance_to(parse_ctx, part.arg_id_end);
internal::format_arg<Range>(parse_ctx, ctx, value.arg_index);
break;
case format_part_t::kind::arg_name:
advance_to(parse_ctx, part.arg_id_end);
internal::format_arg<Range>(parse_ctx, ctx, value.str);
break;
case format_part_t::kind::replacement: {
const auto& arg_id_value = value.repl.arg_id.val;
const auto arg = value.repl.arg_id.kind == arg_id_kind::index
? ctx.arg(arg_id_value.index)
: ctx.arg(arg_id_value.name);
auto specs = value.repl.specs;
handle_dynamic_spec<width_checker>(specs.width, specs.width_ref, ctx);
handle_dynamic_spec<precision_checker>(specs.precision,
specs.precision_ref, ctx);
error_handler h;
numeric_specs_checker<error_handler> checker(h, arg.type());
if (specs.align == align::numeric) checker.require_numeric_argument();
if (specs.sign != sign::none) checker.check_sign();
if (specs.alt) checker.require_numeric_argument();
if (specs.precision >= 0) checker.check_precision();
advance_to(parse_ctx, part.arg_id_end);
ctx.advance_to(
visit_format_arg(arg_formatter<Range>(ctx, nullptr, &specs), arg));
break;
}
}
}
return ctx.out();
}
} // namespace cf
template <typename Char>
void check_prepared_specs(const basic_format_specs<Char>& specs,
internal::type arg_type) const {
internal::error_handler h;
numeric_specs_checker<internal::error_handler> checker(h, arg_type);
if (specs.align == align::numeric) checker.require_numeric_argument();
if (specs.sign != sign::none) checker.check_sign();
if (specs.alt) checker.require_numeric_argument();
if (specs.precision >= 0) checker.check_precision();
}
struct basic_compiled_format {};
private:
Format format_;
PreparedPartsProvider parts_provider_;
};
template <typename S, typename = void>
struct compiled_format_base : basic_compiled_format {
using char_type = char_t<S>;
using parts_container = std::vector<internal::format_part<char_type>>;
template <typename Char> struct part_counter {
unsigned num_parts = 0;
parts_container compiled_parts;
FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
if (begin != end) ++num_parts;
explicit compiled_format_base(basic_string_view<char_type> format_str) {
compile_format_string<false>(format_str,
[this](const format_part<char_type>& part) {
compiled_parts.push_back(part);
});
}
FMT_CONSTEXPR void on_arg_id() { ++num_parts; }
FMT_CONSTEXPR void on_arg_id(unsigned) { ++num_parts; }
FMT_CONSTEXPR void on_arg_id(basic_string_view<Char>) { ++num_parts; }
const parts_container& parts() const { return compiled_parts; }
};
FMT_CONSTEXPR void on_replacement_field(const Char*) {}
template <typename Char, unsigned N> struct format_part_array {
format_part<Char> data[N] = {};
FMT_CONSTEXPR format_part_array() = default;
};
FMT_CONSTEXPR const Char* on_format_specs(const Char* begin,
const Char* end) {
// Find the matching brace.
unsigned braces_counter = 0;
for (; begin != end; ++begin) {
if (*begin == '{') {
++braces_counter;
} else if (*begin == '}') {
if (braces_counter == 0u) break;
--braces_counter;
}
template <typename Char, unsigned N>
FMT_CONSTEXPR format_part_array<Char, N> compile_to_parts(
basic_string_view<Char> format_str) {
format_part_array<Char, N> parts;
unsigned counter = 0;
// This is not a lambda for compatibility with older compilers.
struct {
format_part<Char>* parts;
unsigned* counter;
FMT_CONSTEXPR void operator()(const format_part<Char>& part) {
parts[(*counter)++] = part;
}
return begin;
} collector{parts.data, &counter};
compile_format_string<true>(format_str, collector);
if (counter < N) {
parts.data[counter] =
format_part<Char>::make_text(basic_string_view<Char>());
}
return parts;
}
FMT_CONSTEXPR void on_error(const char*) {}
};
template <typename T> constexpr const T& constexpr_max(const T& a, const T& b) {
return (a < b) ? b : a;
}
template <typename Format> class compiletime_prepared_parts_type_provider {
private:
using char_type = char_t<Format>;
template <typename S>
struct compiled_format_base<S, enable_if_t<is_compile_string<S>::value>>
: basic_compiled_format {
using char_type = char_t<S>;
static FMT_CONSTEXPR unsigned count_parts() {
FMT_CONSTEXPR_DECL const auto text = to_string_view(Format{});
part_counter<char_type> counter;
internal::parse_format_string</*IS_CONSTEXPR=*/true>(text, counter);
return counter.num_parts;
}
FMT_CONSTEXPR explicit compiled_format_base(basic_string_view<char_type>) {}
// Workaround for old compilers. Compiletime parts preparation will not be
// performed with them anyway.
// Workaround for old compilers. Format string compilation will not be
// performed there anyway.
#if FMT_USE_CONSTEXPR
static FMT_CONSTEXPR_DECL const unsigned number_of_format_parts =
compiletime_prepared_parts_type_provider::count_parts();
static FMT_CONSTEXPR_DECL const unsigned num_format_parts =
constexpr_max(count_parts(to_string_view(S())), 1u);
#else
static const unsigned number_of_format_parts = 0u;
static const unsigned num_format_parts = 1;
#endif
using parts_container = format_part<char_type>[num_format_parts];
const parts_container& parts() const {
static FMT_CONSTEXPR_DECL const auto compiled_parts =
compile_to_parts<char_type, num_format_parts>(
internal::to_string_view(S()));
return compiled_parts.data;
}
};
template <typename S, typename... Args>
class compiled_format : private compiled_format_base<S> {
public:
template <unsigned N> struct format_parts_array {
using value_type = format_part<char_type>;
using typename compiled_format_base<S>::char_type;
FMT_CONSTEXPR format_parts_array() : arr{} {}
private:
basic_string_view<char_type> format_str_;
FMT_CONSTEXPR value_type& operator[](unsigned ind) { return arr[ind]; }
template <typename Context, typename Range, typename CompiledFormat>
friend auto cf::vformat_to(Range out, CompiledFormat& cf,
basic_format_args<Context> args) ->
typename Context::iterator;
FMT_CONSTEXPR const value_type* begin() const { return arr; }
FMT_CONSTEXPR const value_type* end() const { return begin() + N; }
public:
compiled_format() = delete;
explicit constexpr compiled_format(basic_string_view<char_type> format_str)
: compiled_format_base<S>(format_str), format_str_(format_str) {}
};
private:
value_type arr[N];
};
#ifdef __cpp_if_constexpr
template <typename... Args> struct type_list {};
// Returns a reference to the argument at index N from [first, rest...].
template <int N, typename T, typename... Args>
constexpr const auto& get(const T& first, const Args&... rest) {
static_assert(N < 1 + sizeof...(Args), "index is out of bounds");
if constexpr (N == 0)
return first;
else
return get<N - 1>(rest...);
}
struct empty {
// Parts preparator will search for it
using value_type = format_part<char_type>;
};
template <int N, typename> struct get_type_impl;
using type = conditional_t<number_of_format_parts != 0,
format_parts_array<number_of_format_parts>, empty>;
template <int N, typename... Args> struct get_type_impl<N, type_list<Args...>> {
using type = remove_cvref_t<decltype(get<N>(std::declval<Args>()...))>;
};
template <typename Parts> class compiletime_prepared_parts_collector {
private:
using format_part = typename Parts::value_type;
template <int N, typename T>
using get_type = typename get_type_impl<N, T>::type;
public:
FMT_CONSTEXPR explicit compiletime_prepared_parts_collector(Parts& parts)
: parts_{parts}, counter_{0u} {}
template <typename Char> struct text {
basic_string_view<Char> data;
using char_type = Char;
FMT_CONSTEXPR void push_back(format_part part) { parts_[counter_++] = part; }
template <typename OutputIt, typename... Args>
OutputIt format(OutputIt out, const Args&...) const {
// TODO: reserve
return copy_str<Char>(data.begin(), data.end(), out);
}
};
FMT_CONSTEXPR format_part& back() { return parts_[counter_ - 1]; }
template <typename Char>
constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos,
size_t size) {
return {{&s[pos], size}};
}
private:
Parts& parts_;
unsigned counter_;
};
template <typename Char, typename OutputIt, typename T,
std::enable_if_t<std::is_integral_v<T>, int> = 0>
OutputIt format_default(OutputIt out, T value) {
// TODO: reserve
format_int fi(value);
return std::copy(fi.data(), fi.data() + fi.size(), out);
}
template <typename PartsContainer, typename Char>
FMT_CONSTEXPR PartsContainer prepare_parts(basic_string_view<Char> format) {
PartsContainer parts;
internal::parse_format_string</*IS_CONSTEXPR=*/false>(
format, format_preparation_handler<Char, PartsContainer>(format, parts));
return parts;
template <typename Char, typename OutputIt>
OutputIt format_default(OutputIt out, double value) {
writer w(out);
w.write(value);
return w.out();
}
template <typename PartsContainer, typename Char>
FMT_CONSTEXPR PartsContainer
prepare_compiletime_parts(basic_string_view<Char> format) {
using collector = compiletime_prepared_parts_collector<PartsContainer>;
template <typename Char, typename OutputIt>
OutputIt format_default(OutputIt out, Char value) {
*out++ = value;
return out;
}
PartsContainer parts;
collector c(parts);
internal::parse_format_string</*IS_CONSTEXPR=*/true>(
format, format_preparation_handler<Char, collector>(format, c));
return parts;
template <typename Char, typename OutputIt>
OutputIt format_default(OutputIt out, const Char* value) {
auto length = std::char_traits<Char>::length(value);
return copy_str<Char>(value, value + length, out);
}
template <typename PartsContainer> class runtime_parts_provider {
public:
runtime_parts_provider() = delete;
template <typename Char>
runtime_parts_provider(basic_string_view<Char> format)
: parts_(prepare_parts<PartsContainer>(format)) {}
// A replacement field that refers to argument N.
template <typename Char, typename T, int N> struct field {
using char_type = Char;
const PartsContainer& parts() const { return parts_; }
template <typename OutputIt, typename... Args>
OutputIt format(OutputIt out, const Args&... args) const {
// This ensures that the argument type is convertile to `const T&`.
const T& arg = get<N>(args...);
return format_default<Char>(out, arg);
}
};
private:
PartsContainer parts_;
template <typename L, typename R> struct concat {
L lhs;
R rhs;
using char_type = typename L::char_type;
template <typename OutputIt, typename... Args>
OutputIt format(OutputIt out, const Args&... args) const {
out = lhs.format(out, args...);
return rhs.format(out, args...);
}
};
template <typename Format, typename PartsContainer>
struct compiletime_parts_provider {
compiletime_parts_provider() = delete;
template <typename Char>
FMT_CONSTEXPR compiletime_parts_provider(basic_string_view<Char>) {}
template <typename L, typename R>
constexpr concat<L, R> make_concat(L lhs, R rhs) {
return {lhs, rhs};
}
const PartsContainer& parts() const {
static FMT_CONSTEXPR_DECL const PartsContainer prepared_parts =
prepare_compiletime_parts<PartsContainer>(
internal::to_string_view(Format{}));
struct unknown_format {};
return prepared_parts;
template <typename Char>
constexpr size_t parse_text(basic_string_view<Char> str, size_t pos) {
for (size_t size = str.size(); pos != size; ++pos) {
if (str[pos] == '{' || str[pos] == '}') break;
}
};
return pos;
}
template <typename Args, size_t POS, int ID, typename S>
constexpr auto compile_format_string(S format_str);
template <typename Args, size_t POS, int ID, typename T, typename S>
constexpr auto parse_tail(T head, S format_str) {
if constexpr (POS != to_string_view(format_str).size()) {
constexpr auto tail = compile_format_string<Args, POS, ID>(format_str);
if constexpr (std::is_same<remove_cvref_t<decltype(tail)>,
unknown_format>())
return tail;
else
return make_concat(head, tail);
} else {
return head;
}
}
// Compiles a non-empty format string and returns the compiled representation
// or unknown_format() on unrecognized input.
template <typename Args, size_t POS, int ID, typename S>
constexpr auto compile_format_string(S format_str) {
using char_type = typename S::char_type;
constexpr basic_string_view<char_type> str = format_str;
if constexpr (str[POS] == '{') {
if (POS + 1 == str.size())
throw format_error("unmatched '{' in format string");
if constexpr (str[POS + 1] == '{') {
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
} else if constexpr (str[POS + 1] == '}') {
using type = get_type<ID, Args>;
if constexpr (std::is_same<type, int>::value) {
return parse_tail<Args, POS + 2, ID + 1>(field<char_type, type, ID>(),
format_str);
} else {
return unknown_format();
}
} else {
return unknown_format();
}
} else if constexpr (str[POS] == '}') {
if (POS + 1 == str.size())
throw format_error("unmatched '}' in format string");
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
} else {
constexpr auto end = parse_text(str, POS + 1);
return parse_tail<Args, end, ID>(make_text(str, POS, end - POS),
format_str);
}
}
#endif // __cpp_if_constexpr
} // namespace internal
#if FMT_USE_CONSTEXPR
# ifdef __cpp_if_constexpr
template <typename... Args, typename S,
FMT_ENABLE_IF(is_compile_string<S>::value)>
FMT_CONSTEXPR auto compile(S format_str) -> internal::prepared_format<
S,
internal::compiletime_parts_provider<
S,
typename internal::compiletime_prepared_parts_type_provider<S>::type>,
Args...> {
return format_str;
constexpr auto compile(S format_str) {
constexpr basic_string_view<typename S::char_type> str = format_str;
if constexpr (str.size() == 0) {
return internal::make_text(str, 0, 0);
} else {
constexpr auto result =
internal::compile_format_string<internal::type_list<Args...>, 0, 0>(
format_str);
if constexpr (std::is_same<remove_cvref_t<decltype(result)>,
internal::unknown_format>()) {
return internal::compiled_format<S, Args...>(to_string_view(format_str));
} else {
return result;
}
}
}
#endif
template <typename CompiledFormat, typename... Args,
typename Char = typename CompiledFormat::char_type,
FMT_ENABLE_IF(!std::is_base_of<internal::basic_compiled_format,
CompiledFormat>::value)>
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
basic_memory_buffer<Char> buffer;
using range = buffer_range<Char>;
using context = buffer_context<Char>;
cf.format(std::back_inserter(buffer), args...);
return to_string(buffer);
}
template <typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(!std::is_base_of<internal::basic_compiled_format,
CompiledFormat>::value)>
OutputIt format_to(OutputIt out, const CompiledFormat& cf,
const Args&... args) {
return cf.format(out, args...);
}
# else
template <typename... Args, typename S,
FMT_ENABLE_IF(is_compile_string<S>::value)>
constexpr auto compile(S format_str) -> internal::compiled_format<S, Args...> {
return internal::compiled_format<S, Args...>(to_string_view(format_str));
}
# endif // __cpp_if_constexpr
#endif // FMT_USE_CONSTEXPR
// Compiles the format string which must be a string literal.
template <typename... Args, typename Char, size_t N>
auto compile(const Char (&format_str)[N]) -> internal::prepared_format<
std::basic_string<Char>,
internal::runtime_parts_provider<std::vector<internal::format_part<Char>>>,
Args...> {
return std::basic_string<Char>(format_str, N - 1);
auto compile(const Char (&format_str)[N])
-> internal::compiled_format<const Char*, Args...> {
return internal::compiled_format<const Char*, Args...>(
basic_string_view<Char>(format_str, N - 1));
}
template <typename CompiledFormat, typename... Args,
typename Char = typename CompiledFormat::char_type>
typename Char = typename CompiledFormat::char_type,
FMT_ENABLE_IF(std::is_base_of<internal::basic_compiled_format,
CompiledFormat>::value)>
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
basic_memory_buffer<Char> buffer;
using range = internal::buffer_range<Char>;
using range = buffer_range<Char>;
using context = buffer_context<Char>;
cf.template vformat_to<range, context>(range(buffer),
{make_format_args<context>(args...)});
internal::cf::vformat_to<context>(range(buffer), cf,
{make_format_args<context>(args...)});
return to_string(buffer);
}
template <typename OutputIt, typename CompiledFormat, typename... Args>
template <typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(std::is_base_of<internal::basic_compiled_format,
CompiledFormat>::value)>
OutputIt format_to(OutputIt out, const CompiledFormat& cf,
const Args&... args) {
using char_type = typename CompiledFormat::char_type;
using range = internal::output_range<OutputIt, char_type>;
using context = format_context_t<OutputIt, char_type>;
return cf.template vformat_to<range, context>(
range(out), {make_format_args<context>(args...)});
return internal::cf::vformat_to<context>(
range(out), cf, {make_format_args<context>(args...)});
}
template <typename OutputIt, typename CompiledFormat, typename... Args,
......@@ -455,10 +579,7 @@ format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
template <typename CompiledFormat, typename... Args>
std::size_t formatted_size(const CompiledFormat& cf, const Args&... args) {
return fmt::format_to(
internal::counting_iterator<typename CompiledFormat::char_type>(),
cf, args...)
.count();
return format_to(internal::counting_iterator(), cf, args...).count();
}
FMT_END_NAMESPACE
......
......@@ -8,7 +8,6 @@
#ifndef FMT_CORE_H_
#define FMT_CORE_H_
#include <cassert>
#include <cstdio> // std::FILE
#include <cstring>
#include <iterator>
......@@ -16,7 +15,7 @@
#include <type_traits>
// The fmt library version in the form major * 10000 + minor * 100 + patch.
#define FMT_VERSION 60000
#define FMT_VERSION 60100
#ifdef __has_feature
# define FMT_HAS_FEATURE(x) __has_feature(x)
......@@ -49,6 +48,12 @@
# define FMT_HAS_GXX_CXX11 0
#endif
#ifdef __NVCC__
# define FMT_NVCC __NVCC__
#else
# define FMT_NVCC 0
#endif
#ifdef _MSC_VER
# define FMT_MSC_VER _MSC_VER
#else
......@@ -60,7 +65,8 @@
#ifndef FMT_USE_CONSTEXPR
# define FMT_USE_CONSTEXPR \
(FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VER >= 1910 || \
(FMT_GCC_VERSION >= 600 && __cplusplus >= 201402L))
(FMT_GCC_VERSION >= 600 && __cplusplus >= 201402L)) && \
!FMT_NVCC
#endif
#if FMT_USE_CONSTEXPR
# define FMT_CONSTEXPR constexpr
......@@ -133,6 +139,13 @@
# endif
#endif
// Workaround broken [[deprecated]] in the Intel compiler and NVCC.
#if defined(__INTEL_COMPILER) || FMT_NVCC
# define FMT_DEPRECATED_ALIAS
#else
# define FMT_DEPRECATED_ALIAS FMT_DEPRECATED
#endif
#ifndef FMT_BEGIN_NAMESPACE
# if FMT_HAS_FEATURE(cxx_inline_namespaces) || FMT_GCC_VERSION >= 404 || \
FMT_MSC_VER >= 1900
......@@ -154,9 +167,9 @@
#if !defined(FMT_HEADER_ONLY) && defined(_WIN32)
# ifdef FMT_EXPORT
# define FMT_API __declspec(dllexport)
# define FMT_API __pragma(warning(suppress : 4275)) __declspec(dllexport)
# elif defined(FMT_SHARED)
# define FMT_API __declspec(dllimport)
# define FMT_API __pragma(warning(suppress : 4275)) __declspec(dllimport)
# define FMT_EXTERN_TEMPLATE_API FMT_API
# endif
#endif
......@@ -173,10 +186,6 @@
# define FMT_EXTERN
#endif
#ifndef FMT_ASSERT
# define FMT_ASSERT(condition, message) assert((condition) && message)
#endif
// libc++ supports string_view in pre-c++17.
#if (FMT_HAS_INCLUDE(<string_view>) && \
(__cplusplus > 201402L || defined(_LIBCPP_VERSION))) || \
......@@ -200,6 +209,8 @@ template <typename T>
using remove_reference_t = typename std::remove_reference<T>::type;
template <typename T>
using remove_const_t = typename std::remove_const<T>::type;
template <typename T>
using remove_cvref_t = typename std::remove_cv<remove_reference_t<T>>::type;
struct monostate {};
......@@ -213,6 +224,19 @@ namespace internal {
// A workaround for gcc 4.8 to make void_t work in a SFINAE context.
template <typename... Ts> struct void_t_impl { using type = void; };
void assert_fail(const char* file, int line, const char* message);
#ifndef FMT_ASSERT
# ifdef NDEBUG
# define FMT_ASSERT(condition, message)
# else
# define FMT_ASSERT(condition, message) \
((condition) \
? void() \
: fmt::internal::assert_fail(__FILE__, __LINE__, (message)))
# endif
#endif
#if defined(FMT_USE_STRING_VIEW)
template <typename Char> using std_string_view = std::basic_string_view<Char>;
#elif defined(FMT_USE_EXPERIMENTAL_STRING_VIEW)
......@@ -222,7 +246,21 @@ using std_string_view = std::experimental::basic_string_view<Char>;
template <typename T> struct std_string_view {};
#endif
// Casts nonnegative integer to unsigned.
#ifdef FMT_USE_INT128
// Do nothing.
#elif defined(__SIZEOF_INT128__)
# define FMT_USE_INT128 1
using int128_t = __int128_t;
using uint128_t = __uint128_t;
#else
# define FMT_USE_INT128 0
#endif
#if !FMT_USE_INT128
struct int128_t {};
struct uint128_t {};
#endif
// Casts a nonnegative integer to unsigned.
template <typename Int>
FMT_CONSTEXPR typename std::make_unsigned<Int>::type to_unsigned(Int value) {
FMT_ASSERT(value >= 0, "negative value");
......@@ -266,10 +304,11 @@ template <typename Char> class basic_string_view {
: data_(s), size_(std::char_traits<Char>::length(s)) {}
/** Constructs a string reference from a ``std::basic_string`` object. */
template <typename Alloc>
FMT_CONSTEXPR basic_string_view(const std::basic_string<Char, Alloc>& s)
FMT_NOEXCEPT : data_(s.data()),
size_(s.size()) {}
template <typename Traits, typename Alloc>
FMT_CONSTEXPR basic_string_view(
const std::basic_string<Char, Traits, Alloc>& s) FMT_NOEXCEPT
: data_(s.data()),
size_(s.size()) {}
template <
typename S,
......@@ -286,6 +325,8 @@ template <typename Char> class basic_string_view {
FMT_CONSTEXPR iterator begin() const { return data_; }
FMT_CONSTEXPR iterator end() const { return data_ + size_; }
FMT_CONSTEXPR const Char& operator[](size_t pos) const { return data_[pos]; }
FMT_CONSTEXPR void remove_prefix(size_t n) {
data_ += n;
size_ -= n;
......@@ -357,10 +398,10 @@ inline basic_string_view<Char> to_string_view(const Char* s) {
return s;
}
template <typename Char, typename Traits, typename Allocator>
template <typename Char, typename Traits, typename Alloc>
inline basic_string_view<Char> to_string_view(
const std::basic_string<Char, Traits, Allocator>& s) {
return {s.data(), s.size()};
const std::basic_string<Char, Traits, Alloc>& s) {
return s;
}
template <typename Char>
......@@ -405,8 +446,8 @@ template <typename S> struct char_t_impl<S, enable_if_t<is_string<S>::value>> {
};
struct error_handler {
FMT_CONSTEXPR error_handler() {}
FMT_CONSTEXPR error_handler(const error_handler&) {}
FMT_CONSTEXPR error_handler() = default;
FMT_CONSTEXPR error_handler(const error_handler&) = default;
// This function is intentionally not constexpr to give a compile-time error.
FMT_NORETURN FMT_API void on_error(const char* message);
......@@ -416,10 +457,24 @@ struct error_handler {
/** String's character type. */
template <typename S> using char_t = typename internal::char_t_impl<S>::type;
// Parsing context consisting of a format string range being parsed and an
// argument counter for automatic indexing.
/**
\rst
Parsing context consisting of a format string range being parsed and an
argument counter for automatic indexing.
You can use one of the following type aliases for common character types:
+-----------------------+-------------------------------------+
| Type | Definition |
+=======================+=====================================+
| format_parse_context | basic_format_parse_context<char> |
+-----------------------+-------------------------------------+
| wformat_parse_context | basic_format_parse_context<wchar_t> |
+-----------------------+-------------------------------------+
\endrst
*/
template <typename Char, typename ErrorHandler = internal::error_handler>
class basic_parse_context : private ErrorHandler {
class basic_format_parse_context : private ErrorHandler {
private:
basic_string_view<Char> format_str_;
int next_arg_id_;
......@@ -428,38 +483,47 @@ class basic_parse_context : private ErrorHandler {
using char_type = Char;
using iterator = typename basic_string_view<Char>::iterator;
explicit FMT_CONSTEXPR basic_parse_context(basic_string_view<Char> format_str,
ErrorHandler eh = ErrorHandler())
explicit FMT_CONSTEXPR basic_format_parse_context(
basic_string_view<Char> format_str, ErrorHandler eh = ErrorHandler())
: ErrorHandler(eh), format_str_(format_str), next_arg_id_(0) {}
// Returns an iterator to the beginning of the format string range being
// parsed.
/**
Returns an iterator to the beginning of the format string range being
parsed.
*/
FMT_CONSTEXPR iterator begin() const FMT_NOEXCEPT {
return format_str_.begin();
}
// Returns an iterator past the end of the format string range being parsed.
/**
Returns an iterator past the end of the format string range being parsed.
*/
FMT_CONSTEXPR iterator end() const FMT_NOEXCEPT { return format_str_.end(); }
// Advances the begin iterator to ``it``.
/** Advances the begin iterator to ``it``. */
FMT_CONSTEXPR void advance_to(iterator it) {
format_str_.remove_prefix(internal::to_unsigned(it - begin()));
}
// Returns the next argument index.
/**
Reports an error if using the manual argument indexing; otherwise returns
the next argument index and switches to the automatic indexing.
*/
FMT_CONSTEXPR int next_arg_id() {
if (next_arg_id_ >= 0) return next_arg_id_++;
on_error("cannot switch from manual to automatic argument indexing");
return 0;
}
FMT_CONSTEXPR bool check_arg_id(int) {
if (next_arg_id_ > 0) {
/**
Reports an error if using the automatic argument indexing; otherwise
switches to the manual indexing.
*/
FMT_CONSTEXPR void check_arg_id(int) {
if (next_arg_id_ > 0)
on_error("cannot switch from automatic to manual argument indexing");
return false;
}
next_arg_id_ = -1;
return true;
else
next_arg_id_ = -1;
}
FMT_CONSTEXPR void check_arg_id(basic_string_view<Char>) {}
......@@ -471,11 +535,14 @@ class basic_parse_context : private ErrorHandler {
FMT_CONSTEXPR ErrorHandler error_handler() const { return *this; }
};
using format_parse_context = basic_parse_context<char>;
using wformat_parse_context = basic_parse_context<wchar_t>;
using format_parse_context = basic_format_parse_context<char>;
using wformat_parse_context = basic_format_parse_context<wchar_t>;
using parse_context FMT_DEPRECATED = basic_parse_context<char>;
using wparse_context FMT_DEPRECATED = basic_parse_context<wchar_t>;
template <typename Char, typename ErrorHandler = internal::error_handler>
using basic_parse_context FMT_DEPRECATED_ALIAS =
basic_format_parse_context<Char, ErrorHandler>;
using parse_context FMT_DEPRECATED_ALIAS = basic_format_parse_context<char>;
using wparse_context FMT_DEPRECATED_ALIAS = basic_format_parse_context<wchar_t>;
template <typename Context> class basic_format_arg;
template <typename Context> class basic_format_args;
......@@ -492,20 +559,17 @@ struct FMT_DEPRECATED convert_to_int
: bool_constant<!std::is_arithmetic<T>::value &&
std::is_convertible<T, int>::value> {};
namespace internal {
// Specifies if T has an enabled formatter specialization. A type can be
// formattable even if it doesn't have a formatter e.g. via a conversion.
template <typename T, typename Context>
using has_formatter =
std::is_constructible<typename Context::template formatter_type<T>>;
namespace internal {
/** A contiguous memory buffer with an optional growing ability. */
template <typename T> class buffer {
private:
buffer(const buffer&) = delete;
void operator=(const buffer&) = delete;
T* ptr_;
std::size_t size_;
std::size_t capacity_;
......@@ -532,7 +596,9 @@ template <typename T> class buffer {
using value_type = T;
using const_reference = const T&;
virtual ~buffer() {}
buffer(const buffer&) = delete;
void operator=(const buffer&) = delete;
virtual ~buffer() = default;
T* begin() FMT_NOEXCEPT { return ptr_; }
T* end() FMT_NOEXCEPT { return ptr_ + size_; }
......@@ -626,10 +692,13 @@ enum type {
uint_type,
long_long_type,
ulong_long_type,
int128_type,
uint128_type,
bool_type,
char_type,
last_integer_type = char_type,
// followed by floating-point types.
float_type,
double_type,
long_double_type,
last_numeric_type = long_double_type,
......@@ -652,20 +721,23 @@ FMT_TYPE_CONSTANT(int, int_type);
FMT_TYPE_CONSTANT(unsigned, uint_type);
FMT_TYPE_CONSTANT(long long, long_long_type);
FMT_TYPE_CONSTANT(unsigned long long, ulong_long_type);
FMT_TYPE_CONSTANT(int128_t, int128_type);
FMT_TYPE_CONSTANT(uint128_t, uint128_type);
FMT_TYPE_CONSTANT(bool, bool_type);
FMT_TYPE_CONSTANT(Char, char_type);
FMT_TYPE_CONSTANT(float, float_type);
FMT_TYPE_CONSTANT(double, double_type);
FMT_TYPE_CONSTANT(long double, long_double_type);
FMT_TYPE_CONSTANT(const Char*, cstring_type);
FMT_TYPE_CONSTANT(basic_string_view<Char>, string_type);
FMT_TYPE_CONSTANT(const void*, pointer_type);
FMT_CONSTEXPR bool is_integral(type t) {
FMT_CONSTEXPR bool is_integral_type(type t) {
FMT_ASSERT(t != named_arg_type, "invalid argument type");
return t > none_type && t <= last_integer_type;
}
FMT_CONSTEXPR bool is_arithmetic(type t) {
FMT_CONSTEXPR bool is_arithmetic_type(type t) {
FMT_ASSERT(t != named_arg_type, "invalid argument type");
return t > none_type && t <= last_numeric_type;
}
......@@ -676,7 +748,7 @@ template <typename Char> struct string_value {
};
template <typename Context> struct custom_value {
using parse_context = basic_parse_context<typename Context::char_type>;
using parse_context = basic_format_parse_context<typename Context::char_type>;
const void* value;
void (*format)(const void* arg, parse_context& parse_ctx, Context& ctx);
};
......@@ -691,8 +763,11 @@ template <typename Context> class value {
unsigned uint_value;
long long long_long_value;
unsigned long long ulong_long_value;
int128_t int128_value;
uint128_t uint128_value;
bool bool_value;
char_type char_value;
float float_value;
double double_value;
long double long_double_value;
const void* pointer;
......@@ -705,6 +780,9 @@ template <typename Context> class value {
FMT_CONSTEXPR value(unsigned val) : uint_value(val) {}
value(long long val) : long_long_value(val) {}
value(unsigned long long val) : ulong_long_value(val) {}
value(int128_t val) : int128_value(val) {}
value(uint128_t val) : uint128_value(val) {}
value(float val) : float_value(val) {}
value(double val) : double_value(val) {}
value(long double val) : long_double_value(val) {}
value(bool val) : bool_value(val) {}
......@@ -732,9 +810,9 @@ template <typename Context> class value {
private:
// Formats an argument of a custom type, such as a user-defined class.
template <typename T, typename Formatter>
static void format_custom_arg(const void* arg,
basic_parse_context<char_type>& parse_ctx,
Context& ctx) {
static void format_custom_arg(
const void* arg, basic_format_parse_context<char_type>& parse_ctx,
Context& ctx) {
Formatter f;
parse_ctx.advance_to(f.parse(parse_ctx));
ctx.advance_to(f.format(*static_cast<const T*>(arg), ctx));
......@@ -764,6 +842,8 @@ template <typename Context> struct arg_mapper {
FMT_CONSTEXPR ulong_type map(unsigned long val) { return val; }
FMT_CONSTEXPR long long map(long long val) { return val; }
FMT_CONSTEXPR unsigned long long map(unsigned long long val) { return val; }
FMT_CONSTEXPR int128_t map(int128_t val) { return val; }
FMT_CONSTEXPR uint128_t map(uint128_t val) { return val; }
FMT_CONSTEXPR bool map(bool val) { return val; }
template <typename T, FMT_ENABLE_IF(is_char<T>::value)>
......@@ -774,7 +854,7 @@ template <typename Context> struct arg_mapper {
return val;
}
FMT_CONSTEXPR double map(float val) { return static_cast<double>(val); }
FMT_CONSTEXPR float map(float val) { return val; }
FMT_CONSTEXPR double map(double val) { return val; }
FMT_CONSTEXPR long double map(long double val) { return val; }
......@@ -793,6 +873,15 @@ template <typename Context> struct arg_mapper {
FMT_CONSTEXPR basic_string_view<char_type> map(const T& val) {
return basic_string_view<char_type>(val);
}
template <
typename T,
FMT_ENABLE_IF(
std::is_constructible<std_string_view<char_type>, T>::value &&
!std::is_constructible<basic_string_view<char_type>, T>::value &&
!is_string<T>::value)>
FMT_CONSTEXPR basic_string_view<char_type> map(const T& val) {
return std_string_view<char_type>(val);
}
FMT_CONSTEXPR const char* map(const signed char* val) {
static_assert(std::is_same<char_type, char>::value, "invalid string type");
return reinterpret_cast<const char*>(val);
......@@ -818,11 +907,14 @@ template <typename Context> struct arg_mapper {
FMT_ENABLE_IF(std::is_enum<T>::value &&
!has_formatter<T, Context>::value &&
!has_fallback_formatter<T, Context>::value)>
FMT_CONSTEXPR int map(const T& val) {
return static_cast<int>(val);
FMT_CONSTEXPR auto map(const T& val) -> decltype(
map(static_cast<typename std::underlying_type<T>::type>(val))) {
return map(static_cast<typename std::underlying_type<T>::type>(val));
}
template <typename T,
FMT_ENABLE_IF(!is_string<T>::value && !is_char<T>::value &&
!std::is_constructible<basic_string_view<char_type>,
T>::value &&
(has_formatter<T, Context>::value ||
has_fallback_formatter<T, Context>::value))>
FMT_CONSTEXPR const T& map(const T& val) {
......@@ -841,12 +933,13 @@ template <typename Context> struct arg_mapper {
// A type constant after applying arg_mapper<Context>.
template <typename T, typename Context>
using mapped_type_constant =
type_constant<decltype(arg_mapper<Context>().map(std::declval<T>())),
type_constant<decltype(arg_mapper<Context>().map(std::declval<const T&>())),
typename Context::char_type>;
enum { packed_arg_bits = 5 };
// Maximum number of arguments with packed types.
enum { max_packed_args = 15 };
enum : unsigned long long { is_unpacked_bit = 1ull << 63 };
enum { max_packed_args = 63 / packed_arg_bits };
enum : unsigned long long { is_unpacked_bit = 1ULL << 63 };
template <typename Context> class arg_map;
} // namespace internal
......@@ -877,7 +970,8 @@ template <typename Context> class basic_format_arg {
public:
explicit handle(internal::custom_value<Context> custom) : custom_(custom) {}
void format(basic_parse_context<char_type>& parse_ctx, Context& ctx) const {
void format(basic_format_parse_context<char_type>& parse_ctx,
Context& ctx) const {
custom_.format(custom_.value, parse_ctx, ctx);
}
......@@ -893,8 +987,8 @@ template <typename Context> class basic_format_arg {
internal::type type() const { return type_; }
bool is_integral() const { return internal::is_integral(type_); }
bool is_arithmetic() const { return internal::is_arithmetic(type_); }
bool is_integral() const { return internal::is_integral_type(type_); }
bool is_arithmetic() const { return internal::is_arithmetic_type(type_); }
};
/**
......@@ -923,10 +1017,22 @@ FMT_CONSTEXPR auto visit_format_arg(Visitor&& vis,
return vis(arg.value_.long_long_value);
case internal::ulong_long_type:
return vis(arg.value_.ulong_long_value);
#if FMT_USE_INT128
case internal::int128_type:
return vis(arg.value_.int128_value);
case internal::uint128_type:
return vis(arg.value_.uint128_value);
#else
case internal::int128_type:
case internal::uint128_type:
break;
#endif
case internal::bool_type:
return vis(arg.value_.bool_value);
case internal::char_type:
return vis(arg.value_.char_value);
case internal::float_type:
return vis(arg.value_.float_value);
case internal::double_type:
return vis(arg.value_.double_value);
case internal::long_double_type:
......@@ -948,9 +1054,6 @@ namespace internal {
// A map from argument names to their values for named arguments.
template <typename Context> class arg_map {
private:
arg_map(const arg_map&) = delete;
void operator=(const arg_map&) = delete;
using char_type = typename Context::char_type;
struct entry {
......@@ -968,6 +1071,8 @@ template <typename Context> class arg_map {
}
public:
arg_map(const arg_map&) = delete;
void operator=(const arg_map&) = delete;
arg_map() : map_(nullptr), size_(0) {}
void init(const basic_format_args<Context>& args);
~arg_map() { delete[] map_; }
......@@ -990,6 +1095,8 @@ class locale_ref {
locale_ref() : locale_(nullptr) {}
template <typename Locale> explicit locale_ref(const Locale& loc);
explicit operator bool() const FMT_NOEXCEPT { return locale_ != nullptr; }
template <typename Locale> Locale get() const;
};
......@@ -998,7 +1105,7 @@ template <typename> constexpr unsigned long long encode_types() { return 0; }
template <typename Context, typename Arg, typename... Args>
constexpr unsigned long long encode_types() {
return mapped_type_constant<Arg, Context>::value |
(encode_types<Context, Args...>() << 4);
(encode_types<Context, Args...>() << packed_arg_bits);
}
template <typename Context, typename T>
......@@ -1034,14 +1141,13 @@ template <typename OutputIt, typename Char> class basic_format_context {
internal::arg_map<basic_format_context> map_;
internal::locale_ref loc_;
basic_format_context(const basic_format_context&) = delete;
void operator=(const basic_format_context&) = delete;
public:
using iterator = OutputIt;
using format_arg = basic_format_arg<basic_format_context>;
template <typename T> using formatter_type = formatter<T, char_type>;
basic_format_context(const basic_format_context&) = delete;
void operator=(const basic_format_context&) = delete;
/**
Constructs a ``basic_format_context`` object. References to the arguments are
stored in the object so make sure they have appropriate lifetimes.
......@@ -1143,8 +1249,9 @@ template <typename Context> class basic_format_args {
bool is_packed() const { return (types_ & internal::is_unpacked_bit) == 0; }
internal::type type(int index) const {
int shift = index * 4;
return static_cast<internal::type>((types_ & (0xfull << shift)) >> shift);
int shift = index * internal::packed_arg_bits;
unsigned int mask = (1 << internal::packed_arg_bits) - 1;
return static_cast<internal::type>((types_ >> shift) & mask);
}
friend class internal::arg_map<Context>;
......@@ -1371,7 +1478,7 @@ inline std::basic_string<Char> format(const S& format_str, Args&&... args) {
}
FMT_API void vprint(std::FILE* f, string_view format_str, format_args args);
FMT_API void vprint(std::FILE* f, wstring_view format_str, wformat_args args);
FMT_API void vprint(string_view format_str, format_args args);
/**
\rst
......@@ -1391,9 +1498,6 @@ inline void print(std::FILE* f, const S& format_str, Args&&... args) {
internal::make_args_checked<Args...>(format_str, args...));
}
FMT_API void vprint(string_view format_str, format_args args);
FMT_API void vprint(wstring_view format_str, wformat_args args);
/**
\rst
Prints formatted data to ``stdout``.
......
// Formatting library for C++
// Formatting library for C++ - implementation
//
// Copyright (c) 2012 - 2016, Victor Zverovich
// All rights reserved.
......@@ -10,14 +10,11 @@
#include "format.h"
#include <string.h>
#include <cassert>
#include <cctype>
#include <cerrno>
#include <climits>
#include <cmath>
#include <cstdarg>
#include <cstddef> // for std::ptrdiff_t
#include <cstring> // for std::memmove
#include <cwchar>
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
......@@ -47,25 +44,22 @@
#ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable : 4127) // conditional expression is constant
# pragma warning(disable : 4702) // unreachable code
// Disable deprecation warning for strerror. The latter is not called but
// MSVC fails to detect it.
# pragma warning(disable : 4996)
#endif
// Dummy implementations of strerror_r and strerror_s called if corresponding
// system functions are not available.
inline fmt::internal::null<> strerror_r(int, char*, ...) {
return fmt::internal::null<>();
}
inline fmt::internal::null<> strerror_s(char*, std::size_t, ...) {
return fmt::internal::null<>();
}
inline fmt::internal::null<> strerror_r(int, char*, ...) { return {}; }
inline fmt::internal::null<> strerror_s(char*, std::size_t, ...) { return {}; }
FMT_BEGIN_NAMESPACE
namespace internal {
FMT_FUNC void assert_fail(const char* file, int line, const char* message) {
print(stderr, "{}:{}: assertion failed: {}", file, line, message);
std::abort();
}
#ifndef _MSC_VER
# define FMT_SNPRINTF snprintf
#else // _MSC_VER
......@@ -81,7 +75,7 @@ inline int fmt_snprintf(char* buffer, size_t size, const char* format, ...) {
using format_func = void (*)(internal::buffer<char>&, int, string_view);
// Portable thread-safe version of strerror.
// A portable thread-safe version of strerror.
// Sets buffer to point to a string describing the error code.
// This can be either a pointer to a string stored in buffer,
// or a pointer to some static immutable string.
......@@ -158,7 +152,7 @@ FMT_FUNC void format_error_code(internal::buffer<char>& out, int error_code,
static const char ERROR_STR[] = "error ";
// Subtract 2 to account for terminating null characters in SEP and ERROR_STR.
std::size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2;
auto abs_value = static_cast<uint32_or_64_t<int>>(error_code);
auto abs_value = static_cast<uint32_or_64_or_128_t<int>>(error_code);
if (internal::is_negative(error_code)) {
abs_value = 0 - abs_value;
++error_code_size;
......@@ -206,6 +200,9 @@ template <typename Locale> Locale locale_ref::get() const {
return locale_ ? *static_cast<const std::locale*>(locale_) : std::locale();
}
template <typename Char> FMT_FUNC std::string grouping_impl(locale_ref loc) {
return std::use_facet<std::numpunct<Char>>(loc.get<std::locale>()).grouping();
}
template <typename Char> FMT_FUNC Char thousands_sep_impl(locale_ref loc) {
return std::use_facet<std::numpunct<Char>>(loc.get<std::locale>())
.thousands_sep();
......@@ -217,6 +214,10 @@ template <typename Char> FMT_FUNC Char decimal_point_impl(locale_ref loc) {
} // namespace internal
#else
template <typename Char>
FMT_FUNC std::string internal::grouping_impl(locale_ref) {
return "\03";
}
template <typename Char>
FMT_FUNC Char internal::thousands_sep_impl(locale_ref) {
return FMT_STATIC_THOUSANDS_SEPARATOR;
}
......@@ -226,8 +227,8 @@ FMT_FUNC Char internal::decimal_point_impl(locale_ref) {
}
#endif
FMT_API FMT_FUNC format_error::~format_error() FMT_NOEXCEPT {}
FMT_API FMT_FUNC system_error::~system_error() FMT_NOEXCEPT {}
FMT_API FMT_FUNC format_error::~format_error() FMT_NOEXCEPT = default;
FMT_API FMT_FUNC system_error::~system_error() FMT_NOEXCEPT = default;
FMT_FUNC void system_error::init(int err_code, string_view format_str,
format_args args) {
......@@ -241,27 +242,13 @@ FMT_FUNC void system_error::init(int err_code, string_view format_str,
namespace internal {
template <> FMT_FUNC int count_digits<4>(internal::fallback_uintptr n) {
// Assume little endian; pointer formatting is implementation-defined anyway.
// fallback_uintptr is always stored in little endian.
int i = static_cast<int>(sizeof(void*)) - 1;
while (i > 0 && n.value[i] == 0) --i;
auto char_digits = std::numeric_limits<unsigned char>::digits / 4;
return i >= 0 ? i * char_digits + count_digits<4, unsigned>(n.value[i]) : 1;
}
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)
throw std::runtime_error(
"fuzz mode - avoid large allocation inside snprintf");
#endif
// Suppress the warning about nonliteral format string.
auto snprintf_ptr = FMT_SNPRINTF;
return precision < 0 ? snprintf_ptr(buf, size, format, value)
: snprintf_ptr(buf, size, format, precision, value);
}
template <typename T>
const char basic_data<T>::digits[] =
"0001020304050607080910111213141516171819"
......@@ -274,14 +261,14 @@ template <typename T>
const char basic_data<T>::hex_digits[] = "0123456789abcdef";
#define FMT_POWERS_OF_10(factor) \
factor * 10, factor * 100, factor * 1000, factor * 10000, factor * 100000, \
factor * 1000000, factor * 10000000, factor * 100000000, \
factor * 1000000000
factor * 10, (factor)*100, (factor)*1000, (factor)*10000, (factor)*100000, \
(factor)*1000000, (factor)*10000000, (factor)*100000000, \
(factor)*1000000000
template <typename T>
const uint64_t basic_data<T>::powers_of_10_64[] = {
1, FMT_POWERS_OF_10(1), FMT_POWERS_OF_10(1000000000ull),
10000000000000000000ull};
1, FMT_POWERS_OF_10(1), FMT_POWERS_OF_10(1000000000ULL),
10000000000000000000ULL};
template <typename T>
const uint32_t basic_data<T>::zero_or_powers_of_10_32[] = {0,
......@@ -289,8 +276,8 @@ const uint32_t basic_data<T>::zero_or_powers_of_10_32[] = {0,
template <typename T>
const uint64_t basic_data<T>::zero_or_powers_of_10_64[] = {
0, FMT_POWERS_OF_10(1), FMT_POWERS_OF_10(1000000000ull),
10000000000000000000ull};
0, FMT_POWERS_OF_10(1), FMT_POWERS_OF_10(1000000000ULL),
10000000000000000000ULL};
// Normalized 64-bit significands of pow(10, k), for k = -348, -340, ..., 340.
// These are generated by support/compute-powers.py.
......@@ -346,12 +333,24 @@ template <typename T>
const char basic_data<T>::background_color[] = "\x1b[48;2;";
template <typename T> const char basic_data<T>::reset_color[] = "\x1b[0m";
template <typename T> const wchar_t basic_data<T>::wreset_color[] = L"\x1b[0m";
template <typename T> const char basic_data<T>::signs[] = {0, '-', '+', ' '};
template <typename T> struct bits {
static FMT_CONSTEXPR_DECL const int value =
static_cast<int>(sizeof(T) * std::numeric_limits<unsigned char>::digits);
};
class fp;
template <int SHIFT = 0> fp normalize(fp value);
// Lower (upper) boundary is a value half way between a floating-point value
// and its predecessor (successor). Boundaries have the same exponent as the
// value so only significands are stored.
struct boundaries {
uint64_t lower;
uint64_t upper;
};
// A handmade floating-point number f * pow(2, e).
class fp {
private:
......@@ -363,7 +362,7 @@ class fp {
static FMT_CONSTEXPR_DECL const int double_significand_size =
std::numeric_limits<double>::digits - 1;
static FMT_CONSTEXPR_DECL const uint64_t implicit_bit =
1ull << double_significand_size;
1ULL << double_significand_size;
public:
significand_type f;
......@@ -377,95 +376,377 @@ class fp {
// Constructs fp from an IEEE754 double. It is a template to prevent compile
// errors on platforms where double is not IEEE754.
template <typename Double> explicit fp(Double d) {
template <typename Double> explicit fp(Double d) { assign(d); }
// Normalizes the value converted from double and multiplied by (1 << SHIFT).
template <int SHIFT> friend fp normalize(fp value) {
// Handle subnormals.
const auto shifted_implicit_bit = fp::implicit_bit << SHIFT;
while ((value.f & shifted_implicit_bit) == 0) {
value.f <<= 1;
--value.e;
}
// Subtract 1 to account for hidden bit.
const auto offset =
fp::significand_size - fp::double_significand_size - SHIFT - 1;
value.f <<= offset;
value.e -= offset;
return value;
}
// Assigns d to this and return true iff predecessor is closer than successor.
template <typename Double, FMT_ENABLE_IF(sizeof(Double) == sizeof(uint64_t))>
bool assign(Double d) {
// Assume double is in the format [sign][exponent][significand].
using limits = std::numeric_limits<Double>;
const int exponent_size =
bits<Double>::value - double_significand_size - 1; // -1 for sign
const uint64_t significand_mask = implicit_bit - 1;
const uint64_t exponent_mask = (~0ull >> 1) & ~significand_mask;
const uint64_t exponent_mask = (~0ULL >> 1) & ~significand_mask;
const int exponent_bias = (1 << exponent_size) - limits::max_exponent - 1;
auto u = bit_cast<uint64_t>(d);
auto biased_e = (u & exponent_mask) >> double_significand_size;
f = u & significand_mask;
auto biased_e = (u & exponent_mask) >> double_significand_size;
// Predecessor is closer if d is a normalized power of 2 (f == 0) other than
// the smallest normalized number (biased_e > 1).
bool is_predecessor_closer = f == 0 && biased_e > 1;
if (biased_e != 0)
f += implicit_bit;
else
biased_e = 1; // Subnormals use biased exponent 1 (min exponent).
e = static_cast<int>(biased_e - exponent_bias - double_significand_size);
return is_predecessor_closer;
}
// Normalizes the value converted from double and multiplied by (1 << SHIFT).
template <int SHIFT = 0> void normalize() {
// Handle subnormals.
auto shifted_implicit_bit = implicit_bit << SHIFT;
while ((f & shifted_implicit_bit) == 0) {
f <<= 1;
--e;
}
// Subtract 1 to account for hidden bit.
auto offset = significand_size - double_significand_size - SHIFT - 1;
f <<= offset;
e -= offset;
template <typename Double, FMT_ENABLE_IF(sizeof(Double) != sizeof(uint64_t))>
bool assign(Double) {
*this = fp();
return false;
}
// Compute lower and upper boundaries (m^- and m^+ in the Grisu paper), where
// a boundary is a value half way between the number and its predecessor
// Assigns d to this together with computing lower and upper boundaries,
// where a boundary is a value half way between the number and its predecessor
// (lower) or successor (upper). The upper boundary is normalized and lower
// has the same exponent but may be not normalized.
void compute_boundaries(fp& lower, fp& upper) const {
lower =
f == implicit_bit ? fp((f << 2) - 1, e - 2) : fp((f << 1) - 1, e - 1);
upper = fp((f << 1) + 1, e - 1);
upper.normalize<1>(); // 1 is to account for the exponent shift above.
template <typename Double> boundaries assign_with_boundaries(Double d) {
bool is_lower_closer = assign(d);
fp lower =
is_lower_closer ? fp((f << 2) - 1, e - 2) : fp((f << 1) - 1, e - 1);
// 1 in normalize accounts for the exponent shift above.
fp upper = normalize<1>(fp((f << 1) + 1, e - 1));
lower.f <<= lower.e - upper.e;
return boundaries{lower.f, upper.f};
}
template <typename Double> boundaries assign_float_with_boundaries(Double d) {
assign(d);
constexpr int min_normal_e = std::numeric_limits<float>::min_exponent -
std::numeric_limits<double>::digits;
significand_type half_ulp = 1 << (std::numeric_limits<double>::digits -
std::numeric_limits<float>::digits - 1);
if (min_normal_e > e) half_ulp <<= min_normal_e - e;
fp upper = normalize<0>(fp(f + half_ulp, e));
fp lower = fp(
f - (half_ulp >> ((f == implicit_bit && e > min_normal_e) ? 1 : 0)), e);
lower.f <<= lower.e - upper.e;
lower.e = upper.e;
return boundaries{lower.f, upper.f};
}
};
// Returns an fp number representing x - y. Result may not be normalized.
inline fp operator-(fp x, fp y) {
FMT_ASSERT(x.f >= y.f && x.e == y.e, "invalid operands");
return fp(x.f - y.f, x.e);
}
inline bool operator==(fp x, fp y) { return x.f == y.f && x.e == y.e; }
// Computes an fp number r with r.f = x.f * y.f / pow(2, 64) rounded to nearest
// with half-up tie breaking, r.e = x.e + y.e + 64. Result may not be
// normalized.
FMT_FUNC fp operator*(fp x, fp y) {
int exp = x.e + y.e + 64;
// Computes lhs * rhs / pow(2, 64) rounded to nearest with half-up tie breaking.
inline uint64_t multiply(uint64_t lhs, uint64_t rhs) {
#if FMT_USE_INT128
auto product = static_cast<__uint128_t>(x.f) * y.f;
auto product = static_cast<__uint128_t>(lhs) * rhs;
auto f = static_cast<uint64_t>(product >> 64);
if ((static_cast<uint64_t>(product) & (1ULL << 63)) != 0) ++f;
return fp(f, exp);
return (static_cast<uint64_t>(product) & (1ULL << 63)) != 0 ? f + 1 : f;
#else
// Multiply 32-bit parts of significands.
uint64_t mask = (1ULL << 32) - 1;
uint64_t a = x.f >> 32, b = x.f & mask;
uint64_t c = y.f >> 32, d = y.f & mask;
uint64_t a = lhs >> 32, b = lhs & mask;
uint64_t c = rhs >> 32, d = rhs & mask;
uint64_t ac = a * c, bc = b * c, ad = a * d, bd = b * d;
// Compute mid 64-bit of result and round.
uint64_t mid = (bd >> 32) + (ad & mask) + (bc & mask) + (1U << 31);
return fp(ac + (ad >> 32) + (bc >> 32) + (mid >> 32), exp);
return ac + (ad >> 32) + (bc >> 32) + (mid >> 32);
#endif
}
// Returns cached power (of 10) c_k = c_k.f * pow(2, c_k.e) such that its
// (binary) exponent satisfies min_exponent <= c_k.e <= min_exponent + 28.
inline fp operator*(fp x, fp y) { return {multiply(x.f, y.f), x.e + y.e + 64}; }
// Returns a cached power of 10 `c_k = c_k.f * pow(2, c_k.e)` such that its
// (binary) exponent satisfies `min_exponent <= c_k.e <= min_exponent + 28`.
FMT_FUNC fp get_cached_power(int min_exponent, int& pow10_exponent) {
const double one_over_log2_10 = 0.30102999566398114; // 1 / log2(10)
const uint64_t one_over_log2_10 = 0x4d104d42; // round(pow(2, 32) / log2(10))
int index = static_cast<int>(
std::ceil((min_exponent + fp::significand_size - 1) * one_over_log2_10));
static_cast<int64_t>(
(min_exponent + fp::significand_size - 1) * one_over_log2_10 +
((uint64_t(1) << 32) - 1) // ceil
) >>
32 // arithmetic shift
);
// Decimal exponent of the first (smallest) cached power of 10.
const int first_dec_exp = -348;
// Difference between 2 consecutive decimal exponents in cached powers of 10.
const int dec_exp_step = 8;
index = (index - first_dec_exp - 1) / dec_exp_step + 1;
pow10_exponent = first_dec_exp + index * dec_exp_step;
return fp(data::pow10_significands[index], data::pow10_exponents[index]);
return {data::pow10_significands[index], data::pow10_exponents[index]};
}
// A simple accumulator to hold the sums of terms in bigint::square if uint128_t
// is not available.
struct accumulator {
uint64_t lower;
uint64_t upper;
accumulator() : lower(0), upper(0) {}
explicit operator uint32_t() const { return static_cast<uint32_t>(lower); }
void operator+=(uint64_t n) {
lower += n;
if (lower < n) ++upper;
}
void operator>>=(int shift) {
assert(shift == 32);
(void)shift;
lower = (upper << 32) | (lower >> 32);
upper >>= 32;
}
};
class bigint {
private:
// A bigint is stored as an array of bigits (big digits), with bigit at index
// 0 being the least significant one.
using bigit = uint32_t;
using double_bigit = uint64_t;
enum { bigits_capacity = 32 };
basic_memory_buffer<bigit, bigits_capacity> bigits_;
int exp_;
static FMT_CONSTEXPR_DECL const int bigit_bits = bits<bigit>::value;
friend struct formatter<bigint>;
void subtract_bigits(int index, bigit other, bigit& borrow) {
auto result = static_cast<double_bigit>(bigits_[index]) - other - borrow;
bigits_[index] = static_cast<bigit>(result);
borrow = static_cast<bigit>(result >> (bigit_bits * 2 - 1));
}
void remove_leading_zeros() {
int num_bigits = static_cast<int>(bigits_.size()) - 1;
while (num_bigits > 0 && bigits_[num_bigits] == 0) --num_bigits;
bigits_.resize(num_bigits + 1);
}
// Computes *this -= other assuming aligned bigints and *this >= other.
void subtract_aligned(const bigint& other) {
FMT_ASSERT(other.exp_ >= exp_, "unaligned bigints");
FMT_ASSERT(compare(*this, other) >= 0, "");
bigit borrow = 0;
int i = other.exp_ - exp_;
for (int j = 0, n = static_cast<int>(other.bigits_.size()); j != n;
++i, ++j) {
subtract_bigits(i, other.bigits_[j], borrow);
}
while (borrow > 0) subtract_bigits(i, 0, borrow);
remove_leading_zeros();
}
void multiply(uint32_t value) {
const double_bigit wide_value = value;
bigit carry = 0;
for (size_t i = 0, n = bigits_.size(); i < n; ++i) {
double_bigit result = bigits_[i] * wide_value + carry;
bigits_[i] = static_cast<bigit>(result);
carry = static_cast<bigit>(result >> bigit_bits);
}
if (carry != 0) bigits_.push_back(carry);
}
void multiply(uint64_t value) {
const bigit mask = ~bigit(0);
const double_bigit lower = value & mask;
const double_bigit upper = value >> bigit_bits;
double_bigit carry = 0;
for (size_t i = 0, n = bigits_.size(); i < n; ++i) {
double_bigit result = bigits_[i] * lower + (carry & mask);
carry =
bigits_[i] * upper + (result >> bigit_bits) + (carry >> bigit_bits);
bigits_[i] = static_cast<bigit>(result);
}
while (carry != 0) {
bigits_.push_back(carry & mask);
carry >>= bigit_bits;
}
}
public:
bigint() : exp_(0) {}
explicit bigint(uint64_t n) { assign(n); }
~bigint() { assert(bigits_.capacity() <= bigits_capacity); }
bigint(const bigint&) = delete;
void operator=(const bigint&) = delete;
void assign(const bigint& other) {
bigits_.resize(other.bigits_.size());
auto data = other.bigits_.data();
std::copy(data, data + other.bigits_.size(), bigits_.data());
exp_ = other.exp_;
}
void assign(uint64_t n) {
int num_bigits = 0;
do {
bigits_[num_bigits++] = n & ~bigit(0);
n >>= bigit_bits;
} while (n != 0);
bigits_.resize(num_bigits);
exp_ = 0;
}
int num_bigits() const { return static_cast<int>(bigits_.size()) + exp_; }
bigint& operator<<=(int shift) {
assert(shift >= 0);
exp_ += shift / bigit_bits;
shift %= bigit_bits;
if (shift == 0) return *this;
bigit carry = 0;
for (size_t i = 0, n = bigits_.size(); i < n; ++i) {
bigit c = bigits_[i] >> (bigit_bits - shift);
bigits_[i] = (bigits_[i] << shift) + carry;
carry = c;
}
if (carry != 0) bigits_.push_back(carry);
return *this;
}
template <typename Int> bigint& operator*=(Int value) {
FMT_ASSERT(value > 0, "");
multiply(uint32_or_64_or_128_t<Int>(value));
return *this;
}
friend int compare(const bigint& lhs, const bigint& rhs) {
int num_lhs_bigits = lhs.num_bigits(), num_rhs_bigits = rhs.num_bigits();
if (num_lhs_bigits != num_rhs_bigits)
return num_lhs_bigits > num_rhs_bigits ? 1 : -1;
int i = static_cast<int>(lhs.bigits_.size()) - 1;
int j = static_cast<int>(rhs.bigits_.size()) - 1;
int end = i - j;
if (end < 0) end = 0;
for (; i >= end; --i, --j) {
bigit lhs_bigit = lhs.bigits_[i], rhs_bigit = rhs.bigits_[j];
if (lhs_bigit != rhs_bigit) return lhs_bigit > rhs_bigit ? 1 : -1;
}
if (i != j) return i > j ? 1 : -1;
return 0;
}
// Returns compare(lhs1 + lhs2, rhs).
friend int add_compare(const bigint& lhs1, const bigint& lhs2,
const bigint& rhs) {
int max_lhs_bigits = (std::max)(lhs1.num_bigits(), lhs2.num_bigits());
int num_rhs_bigits = rhs.num_bigits();
if (max_lhs_bigits + 1 < num_rhs_bigits) return -1;
if (max_lhs_bigits > num_rhs_bigits) return 1;
auto get_bigit = [](const bigint& n, int i) -> bigit {
return i >= n.exp_ && i < n.num_bigits() ? n.bigits_[i - n.exp_] : 0;
};
double_bigit borrow = 0;
int min_exp = (std::min)((std::min)(lhs1.exp_, lhs2.exp_), rhs.exp_);
for (int i = num_rhs_bigits - 1; i >= min_exp; --i) {
double_bigit sum =
static_cast<double_bigit>(get_bigit(lhs1, i)) + get_bigit(lhs2, i);
bigit rhs_bigit = get_bigit(rhs, i);
if (sum > rhs_bigit + borrow) return 1;
borrow = rhs_bigit + borrow - sum;
if (borrow > 1) return -1;
borrow <<= bigit_bits;
}
return borrow != 0 ? -1 : 0;
}
// Assigns pow(10, exp) to this bigint.
void assign_pow10(int exp) {
assert(exp >= 0);
if (exp == 0) return assign(1);
// Find the top bit.
int bitmask = 1;
while (exp >= bitmask) bitmask <<= 1;
bitmask >>= 1;
// pow(10, exp) = pow(5, exp) * pow(2, exp). First compute pow(5, exp) by
// repeated squaring and multiplication.
assign(5);
bitmask >>= 1;
while (bitmask != 0) {
square();
if ((exp & bitmask) != 0) *this *= 5;
bitmask >>= 1;
}
*this <<= exp; // Multiply by pow(2, exp) by shifting.
}
void square() {
basic_memory_buffer<bigit, bigits_capacity> n(std::move(bigits_));
int num_bigits = static_cast<int>(bigits_.size());
int num_result_bigits = 2 * num_bigits;
bigits_.resize(num_result_bigits);
using accumulator_t = conditional_t<FMT_USE_INT128, uint128_t, accumulator>;
auto sum = accumulator_t();
for (int bigit_index = 0; bigit_index < num_bigits; ++bigit_index) {
// Compute bigit at position bigit_index of the result by adding
// cross-product terms n[i] * n[j] such that i + j == bigit_index.
for (int i = 0, j = bigit_index; j >= 0; ++i, --j) {
// Most terms are multiplied twice which can be optimized in the future.
sum += static_cast<double_bigit>(n[i]) * n[j];
}
bigits_[bigit_index] = static_cast<bigit>(sum);
sum >>= bits<bigit>::value; // Compute the carry.
}
// Do the same for the top half.
for (int bigit_index = num_bigits; bigit_index < num_result_bigits;
++bigit_index) {
for (int j = num_bigits - 1, i = bigit_index - j; i < num_bigits;)
sum += static_cast<double_bigit>(n[i++]) * n[j--];
bigits_[bigit_index] = static_cast<bigit>(sum);
sum >>= bits<bigit>::value;
}
--num_result_bigits;
remove_leading_zeros();
exp_ *= 2;
}
// Divides this bignum by divisor, assigning the remainder to this and
// returning the quotient.
int divmod_assign(const bigint& divisor) {
FMT_ASSERT(this != &divisor, "");
if (compare(*this, divisor) < 0) return 0;
int num_bigits = static_cast<int>(bigits_.size());
FMT_ASSERT(divisor.bigits_[divisor.bigits_.size() - 1] != 0, "");
int exp_difference = exp_ - divisor.exp_;
if (exp_difference > 0) {
// Align bigints by adding trailing zeros to simplify subtraction.
bigits_.resize(num_bigits + exp_difference);
for (int i = num_bigits - 1, j = i + exp_difference; i >= 0; --i, --j)
bigits_[j] = bigits_[i];
std::uninitialized_fill_n(bigits_.data(), exp_difference, 0);
exp_ -= exp_difference;
}
int quotient = 0;
do {
subtract_aligned(divisor);
++quotient;
} while (compare(*this, divisor) >= 0);
return quotient;
}
};
enum round_direction { unknown, up, down };
// Given the divisor (normally a power of 10), the remainder = v % divisor for
......@@ -500,13 +781,13 @@ enum result {
// error: the size of the region (lower, upper) outside of which numbers
// definitely do not round to value (Delta in Grisu3).
template <typename Handler>
digits::result grisu_gen_digits(fp value, uint64_t error, int& exp,
Handler& handler) {
fp one(1ull << -value.e, value.e);
FMT_ALWAYS_INLINE digits::result grisu_gen_digits(fp value, uint64_t error,
int& exp, Handler& handler) {
const fp one(1ULL << -value.e, value.e);
// The integral part of scaled value (p1 in Grisu) = value / one. It cannot be
// zero because it contains a product of two 64-bit numbers with MSB set (due
// to normalization) - 1, shifted right by at most 60 bits.
uint32_t integral = static_cast<uint32_t>(value.f >> -one.e);
auto integral = static_cast<uint32_t>(value.f >> -one.e);
FMT_ASSERT(integral != 0, "");
FMT_ASSERT(integral == value.f >> -one.e, "");
// The fractional part of scaled value (p2 in Grisu) c = value % one.
......@@ -519,44 +800,39 @@ digits::result grisu_gen_digits(fp value, uint64_t error, int& exp,
// Generate digits for the integral part. This can produce up to 10 digits.
do {
uint32_t digit = 0;
// This optimization by miloyip reduces the number of integer divisions by
auto divmod_integral = [&](uint32_t divisor) {
digit = integral / divisor;
integral %= divisor;
};
// This optimization by Milo Yip reduces the number of integer divisions by
// one per iteration.
switch (exp) {
case 10:
digit = integral / 1000000000;
integral %= 1000000000;
divmod_integral(1000000000);
break;
case 9:
digit = integral / 100000000;
integral %= 100000000;
divmod_integral(100000000);
break;
case 8:
digit = integral / 10000000;
integral %= 10000000;
divmod_integral(10000000);
break;
case 7:
digit = integral / 1000000;
integral %= 1000000;
divmod_integral(1000000);
break;
case 6:
digit = integral / 100000;
integral %= 100000;
divmod_integral(100000);
break;
case 5:
digit = integral / 10000;
integral %= 10000;
divmod_integral(10000);
break;
case 4:
digit = integral / 1000;
integral %= 1000;
divmod_integral(1000);
break;
case 3:
digit = integral / 100;
integral %= 100;
divmod_integral(100);
break;
case 2:
digit = integral / 10;
integral %= 10;
divmod_integral(10);
break;
case 1:
digit = integral;
......@@ -640,7 +916,7 @@ struct fixed_handler {
};
// The shortest representation digit handler.
template <int GRISU_VERSION> struct grisu_shortest_handler {
struct grisu_shortest_handler {
char* buf;
int size;
// Distance between scaled value and upper bound (wp_W in Grisu3).
......@@ -666,11 +942,6 @@ template <int GRISU_VERSION> struct grisu_shortest_handler {
uint64_t error, int exp, bool integral) {
buf[size++] = digit;
if (remainder >= error) return digits::more;
if (GRISU_VERSION != 3) {
uint64_t d = integral ? diff : diff * data::powers_of_10_64[-exp];
round(d, divisor, remainder, error);
return digits::done;
}
uint64_t unit = integral ? 1 : data::powers_of_10_64[-exp];
uint64_t up = (diff - 1) * unit; // wp_Wup
round(up, divisor, remainder, error);
......@@ -686,151 +957,289 @@ template <int GRISU_VERSION> struct grisu_shortest_handler {
}
};
template <typename Double,
enable_if_t<(sizeof(Double) == sizeof(uint64_t)), int>>
FMT_API bool grisu_format(Double value, buffer<char>& buf, int precision,
unsigned options, int& exp) {
// Formats value using a variation of the Fixed-Precision Positive
// Floating-Point Printout ((FPP)^2) algorithm by Steele & White:
// https://fmt.dev/p372-steele.pdf.
template <typename Double>
void fallback_format(Double d, buffer<char>& buf, int& exp10) {
bigint numerator; // 2 * R in (FPP)^2.
bigint denominator; // 2 * S in (FPP)^2.
// lower and upper are differences between value and corresponding boundaries.
bigint lower; // (M^- in (FPP)^2).
bigint upper_store; // upper's value if different from lower.
bigint* upper = nullptr; // (M^+ in (FPP)^2).
fp value;
// Shift numerator and denominator by an extra bit or two (if lower boundary
// is closer) to make lower and upper integers. This eliminates multiplication
// by 2 during later computations.
// TODO: handle float
int shift = value.assign(d) ? 2 : 1;
uint64_t significand = value.f << shift;
if (value.e >= 0) {
numerator.assign(significand);
numerator <<= value.e;
lower.assign(1);
lower <<= value.e;
if (shift != 1) {
upper_store.assign(1);
upper_store <<= value.e + 1;
upper = &upper_store;
}
denominator.assign_pow10(exp10);
denominator <<= 1;
} else if (exp10 < 0) {
numerator.assign_pow10(-exp10);
lower.assign(numerator);
if (shift != 1) {
upper_store.assign(numerator);
upper_store <<= 1;
upper = &upper_store;
}
numerator *= significand;
denominator.assign(1);
denominator <<= shift - value.e;
} else {
numerator.assign(significand);
denominator.assign_pow10(exp10);
denominator <<= shift - value.e;
lower.assign(1);
if (shift != 1) {
upper_store.assign(1ULL << 1);
upper = &upper_store;
}
}
if (!upper) upper = &lower;
// Invariant: value == (numerator / denominator) * pow(10, exp10).
bool even = (value.f & 1) == 0;
int num_digits = 0;
char* data = buf.data();
for (;;) {
int digit = numerator.divmod_assign(denominator);
bool low = compare(numerator, lower) - even < 0; // numerator <[=] lower.
// numerator + upper >[=] pow10:
bool high = add_compare(numerator, *upper, denominator) + even > 0;
data[num_digits++] = static_cast<char>('0' + digit);
if (low || high) {
if (!low) {
++data[num_digits - 1];
} else if (high) {
int result = add_compare(numerator, numerator, denominator);
// Round half to even.
if (result > 0 || (result == 0 && (digit % 2) != 0))
++data[num_digits - 1];
}
buf.resize(num_digits);
exp10 -= num_digits - 1;
return;
}
numerator *= 10;
lower *= 10;
if (upper != &lower) *upper *= 10;
}
}
// Formats value using the Grisu algorithm
// (https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf)
// if T is a IEEE754 binary32 or binary64 and snprintf otherwise.
template <typename T>
int format_float(T value, int precision, float_specs specs, buffer<char>& buf) {
static_assert(!std::is_same<T, float>(), "");
FMT_ASSERT(value >= 0, "value is negative");
bool fixed = (options & grisu_options::fixed) != 0;
const bool fixed = specs.format == float_format::fixed;
if (value <= 0) { // <= instead of == to silence a warning.
if (precision <= 0 || !fixed) {
exp = 0;
buf.push_back('0');
} else {
exp = -precision;
buf.resize(to_unsigned(precision));
std::uninitialized_fill_n(buf.data(), precision, '0');
return 0;
}
return true;
buf.resize(to_unsigned(precision));
std::uninitialized_fill_n(buf.data(), precision, '0');
return -precision;
}
fp fp_value(value);
if (!specs.use_grisu) return snprintf_float(value, precision, specs, buf);
int exp = 0;
const int min_exp = -60; // alpha in Grisu.
int cached_exp10 = 0; // K in Grisu.
if (precision != -1) {
if (precision > 17) return false;
fp_value.normalize();
auto cached_pow = get_cached_power(
min_exp - (fp_value.e + fp::significand_size), cached_exp10);
fp_value = fp_value * cached_pow;
if (precision > 17) return snprintf_float(value, precision, specs, buf);
fp normalized = normalize(fp(value));
const auto cached_pow = get_cached_power(
min_exp - (normalized.e + fp::significand_size), cached_exp10);
normalized = normalized * cached_pow;
fixed_handler handler{buf.data(), 0, precision, -cached_exp10, fixed};
if (grisu_gen_digits(fp_value, 1, exp, handler) == digits::error)
return false;
buf.resize(to_unsigned(handler.size));
if (grisu_gen_digits(normalized, 1, exp, handler) == digits::error)
return snprintf_float(value, precision, specs, buf);
int num_digits = handler.size;
if (!fixed) {
// Remove trailing zeros.
while (num_digits > 0 && buf[num_digits - 1] == '0') {
--num_digits;
++exp;
}
}
buf.resize(to_unsigned(num_digits));
} else {
fp lower, upper; // w^- and w^+ in the Grisu paper.
fp_value.compute_boundaries(lower, upper);
// Find a cached power of 10 such that multiplying upper by it will bring
fp fp_value;
auto boundaries = specs.binary32
? fp_value.assign_float_with_boundaries(value)
: fp_value.assign_with_boundaries(value);
fp_value = normalize(fp_value);
// Find a cached power of 10 such that multiplying value by it will bring
// the exponent in the range [min_exp, -32].
auto cached_pow = get_cached_power( // \tilde{c}_{-k} in Grisu.
min_exp - (upper.e + fp::significand_size), cached_exp10);
fp_value.normalize();
const fp cached_pow = get_cached_power(
min_exp - (fp_value.e + fp::significand_size), cached_exp10);
// Multiply value and boundaries by the cached power of 10.
fp_value = fp_value * cached_pow;
lower = lower * cached_pow; // \tilde{M}^- in Grisu.
upper = upper * cached_pow; // \tilde{M}^+ in Grisu.
assert(min_exp <= upper.e && upper.e <= -32);
auto result = digits::result();
int size = 0;
if ((options & grisu_options::grisu3) != 0) {
--lower.f; // \tilde{M}^- - 1 ulp -> M^-_{\downarrow}.
++upper.f; // \tilde{M}^+ + 1 ulp -> M^+_{\uparrow}.
// Numbers outside of (lower, upper) definitely do not round to value.
grisu_shortest_handler<3> handler{buf.data(), 0, (upper - fp_value).f};
result = grisu_gen_digits(upper, upper.f - lower.f, exp, handler);
size = handler.size;
} else {
++lower.f; // \tilde{M}^- + 1 ulp -> M^-_{\uparrow}.
--upper.f; // \tilde{M}^+ - 1 ulp -> M^+_{\downarrow}.
grisu_shortest_handler<2> handler{buf.data(), 0, (upper - fp_value).f};
result = grisu_gen_digits(upper, upper.f - lower.f, exp, handler);
size = handler.size;
boundaries.lower = multiply(boundaries.lower, cached_pow.f);
boundaries.upper = multiply(boundaries.upper, cached_pow.f);
assert(min_exp <= fp_value.e && fp_value.e <= -32);
--boundaries.lower; // \tilde{M}^- - 1 ulp -> M^-_{\downarrow}.
++boundaries.upper; // \tilde{M}^+ + 1 ulp -> M^+_{\uparrow}.
// Numbers outside of (lower, upper) definitely do not round to value.
grisu_shortest_handler handler{buf.data(), 0,
boundaries.upper - fp_value.f};
auto result =
grisu_gen_digits(fp(boundaries.upper, fp_value.e),
boundaries.upper - boundaries.lower, exp, handler);
if (result == digits::error) {
exp += handler.size - cached_exp10 - 1;
fallback_format(value, buf, exp);
return exp;
}
if (result == digits::error) return false;
buf.resize(to_unsigned(size));
buf.resize(to_unsigned(handler.size));
}
exp -= cached_exp10;
return true;
return exp - cached_exp10;
}
template <typename Double>
char* sprintf_format(Double value, internal::buffer<char>& buf,
sprintf_specs specs) {
template <typename T>
int snprintf_float(T value, int precision, float_specs specs,
buffer<char>& buf) {
// Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail.
FMT_ASSERT(buf.capacity() != 0, "empty buffer");
FMT_ASSERT(buf.capacity() > buf.size(), "empty buffer");
static_assert(!std::is_same<T, float>(), "");
// Build format string.
enum { max_format_size = 10 }; // longest format: %#-*.*Lg
// Subtract 1 to account for the difference in precision since we use %e for
// both general and exponent format.
if (specs.format == float_format::general ||
specs.format == float_format::exp)
precision = (precision >= 0 ? precision : 6) - 1;
// Build the format string.
enum { max_format_size = 7 }; // Ths longest format is "%#.*Le".
char format[max_format_size];
char* format_ptr = format;
*format_ptr++ = '%';
if (specs.alt || !specs.type) *format_ptr++ = '#';
if (specs.precision >= 0) {
if (specs.trailing_zeros) *format_ptr++ = '#';
if (precision >= 0) {
*format_ptr++ = '.';
*format_ptr++ = '*';
}
if (std::is_same<Double, long double>::value) *format_ptr++ = 'L';
char type = specs.type;
if (type == '%')
type = 'f';
else if (type == 0 || type == 'n')
type = 'g';
#if FMT_MSC_VER
if (type == 'F') {
// MSVC's printf doesn't support 'F'.
type = 'f';
}
#endif
*format_ptr++ = type;
if (std::is_same<T, long double>()) *format_ptr++ = 'L';
*format_ptr++ = specs.format != float_format::hex
? (specs.format == float_format::fixed ? 'f' : 'e')
: (specs.upper ? 'A' : 'a');
*format_ptr = '\0';
// Format using snprintf.
char* start = nullptr;
char* decimal_point_pos = nullptr;
auto offset = buf.size();
for (;;) {
std::size_t buffer_size = buf.capacity();
start = &buf[0];
int result =
format_float(start, buffer_size, format, specs.precision, value);
if (result >= 0) {
unsigned n = internal::to_unsigned(result);
if (n < buf.capacity()) {
// Find the decimal point.
auto p = buf.data(), end = p + n;
if (*p == '+' || *p == '-') ++p;
if (specs.type != 'a' && specs.type != 'A') {
while (p < end && *p >= '0' && *p <= '9') ++p;
if (p < end && *p != 'e' && *p != 'E') {
decimal_point_pos = p;
if (!specs.type) {
// Keep only one trailing zero after the decimal point.
++p;
if (*p == '0') ++p;
while (p != end && *p >= '1' && *p <= '9') ++p;
char* where = p;
while (p != end && *p == '0') ++p;
if (p == end || *p < '0' || *p > '9') {
if (p != end) std::memmove(where, p, to_unsigned(end - p));
n -= static_cast<unsigned>(p - where);
}
}
}
}
buf.resize(n);
break; // The buffer is large enough - continue with formatting.
auto begin = buf.data() + offset;
auto capacity = buf.capacity() - offset;
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
if (precision > 100000)
throw std::runtime_error(
"fuzz mode - avoid large allocation inside snprintf");
#endif
// Suppress the warning about a nonliteral format string.
auto snprintf_ptr = FMT_SNPRINTF;
int result = precision >= 0
? snprintf_ptr(begin, capacity, format, precision, value)
: snprintf_ptr(begin, capacity, format, value);
if (result < 0) {
buf.reserve(buf.capacity() + 1); // The buffer will grow exponentially.
continue;
}
unsigned size = to_unsigned(result);
// Size equal to capacity means that the last character was truncated.
if (size >= capacity) {
buf.reserve(size + offset + 1); // Add 1 for the terminating '\0'.
continue;
}
auto is_digit = [](char c) { return c >= '0' && c <= '9'; };
if (specs.format == float_format::fixed) {
if (precision == 0) {
buf.resize(size);
return 0;
}
buf.reserve(n + 1);
} else {
// If result is negative we ask to increase the capacity by at least 1,
// but as std::vector, the buffer grows exponentially.
buf.reserve(buf.capacity() + 1);
// Find and remove the decimal point.
auto end = begin + size, p = end;
do {
--p;
} while (is_digit(*p));
int fraction_size = static_cast<int>(end - p - 1);
std::memmove(p, p + 1, fraction_size);
buf.resize(size - 1);
return -fraction_size;
}
if (specs.format == float_format::hex) {
buf.resize(size + offset);
return 0;
}
// Find and parse the exponent.
auto end = begin + size, exp_pos = end;
do {
--exp_pos;
} while (*exp_pos != 'e');
char sign = exp_pos[1];
assert(sign == '+' || sign == '-');
int exp = 0;
auto p = exp_pos + 2; // Skip 'e' and sign.
do {
assert(is_digit(*p));
exp = exp * 10 + (*p++ - '0');
} while (p != end);
if (sign == '-') exp = -exp;
int fraction_size = 0;
if (exp_pos != begin + 1) {
// Remove trailing zeros.
auto fraction_end = exp_pos - 1;
while (*fraction_end == '0') --fraction_end;
// Move the fractional part left to get rid of the decimal point.
fraction_size = static_cast<int>(fraction_end - begin - 1);
std::memmove(begin + 1, begin + 2, fraction_size);
}
buf.resize(fraction_size + offset + 1);
return exp - fraction_size;
}
return decimal_point_pos;
}
} // namespace internal
template <> struct formatter<internal::bigint> {
format_parse_context::iterator parse(format_parse_context& ctx) {
return ctx.begin();
}
format_context::iterator format(const internal::bigint& n,
format_context& ctx) {
auto out = ctx.out();
bool first = true;
for (auto i = n.bigits_.size(); i > 0; --i) {
auto value = n.bigits_[i - 1];
if (first) {
out = format_to(out, "{:x}", value);
first = false;
continue;
}
out = format_to(out, "{:08x}", value);
}
if (n.exp_ > 0)
out = format_to(out, "p{}", n.exp_ * internal::bigint::bigit_bits);
return out;
}
};
#if FMT_USE_WINDOWS_H
FMT_FUNC internal::utf8_to_utf16::utf8_to_utf16(string_view s) {
......@@ -974,23 +1383,10 @@ FMT_FUNC void vprint(std::FILE* f, string_view format_str, format_args args) {
internal::fwrite_fully(buffer.data(), 1, buffer.size(), f);
}
FMT_FUNC void vprint(std::FILE* f, wstring_view format_str, wformat_args args) {
wmemory_buffer buffer;
internal::vformat_to(buffer, format_str, args);
buffer.push_back(L'\0');
if (std::fputws(buffer.data(), f) == -1) {
FMT_THROW(system_error(errno, "cannot write to file"));
}
}
FMT_FUNC void vprint(string_view format_str, format_args args) {
vprint(stdout, format_str, args);
}
FMT_FUNC void vprint(wstring_view format_str, wformat_args args) {
vprint(stdout, format_str, args);
}
FMT_END_NAMESPACE
#ifdef _MSC_VER
......
......@@ -33,18 +33,16 @@
#ifndef FMT_FORMAT_H_
#define FMT_FORMAT_H_
#include "core.h"
#include <algorithm>
#include <cassert>
#include <cerrno>
#include <cmath>
#include <cstdint>
#include <cstring>
#include <iterator>
#include <limits>
#include <memory>
#include <stdexcept>
#include "core.h"
#ifdef __clang__
# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__)
#else
......@@ -71,6 +69,12 @@
# define FMT_HAS_BUILTIN(x) 0
#endif
#if FMT_HAS_CPP_ATTRIBUTE(fallthrough) >= 201603 && __cplusplus >= 201703
# define FMT_FALLTHROUGH [[fallthrough]]
#else
# define FMT_FALLTHROUGH
#endif
#ifndef FMT_THROW
# if FMT_EXCEPTIONS
# if FMT_MSC_VER
......@@ -84,7 +88,7 @@ template <typename Exception> inline void do_throw(const Exception& x) {
}
} // namespace internal
FMT_END_NAMESPACE
# define FMT_THROW(x) fmt::internal::do_throw(x)
# define FMT_THROW(x) internal::do_throw(x)
# else
# define FMT_THROW(x) throw x
# endif
......@@ -92,7 +96,7 @@ FMT_END_NAMESPACE
# define FMT_THROW(x) \
do { \
static_cast<void>(sizeof(x)); \
assert(false); \
FMT_ASSERT(false, ""); \
} while (false)
# endif
#endif
......@@ -123,14 +127,6 @@ FMT_END_NAMESPACE
# endif
#endif
#ifdef FMT_USE_INT128
// Do nothing.
#elif defined(__SIZEOF_INT128__)
# define FMT_USE_INT128 1
#else
# define FMT_USE_INT128 0
#endif
// __builtin_clz is broken in clang with Microsoft CodeGen:
// https://github.com/fmtlib/fmt/issues/519
#if (FMT_GCC_VERSION || FMT_HAS_BUILTIN(__builtin_clz)) && !FMT_MSC_VER
......@@ -156,14 +152,14 @@ inline uint32_t clz(uint32_t x) {
unsigned long r = 0;
_BitScanReverse(&r, x);
assert(x != 0);
FMT_ASSERT(x != 0, "");
// Static analysis complains about using uninitialized data
// "r", but the only way that can happen is if "x" is 0,
// which the callers guarantee to not happen.
# pragma warning(suppress : 6102)
return 31 - r;
}
# define FMT_BUILTIN_CLZ(n) fmt::internal::clz(n)
# define FMT_BUILTIN_CLZ(n) internal::clz(n)
# if defined(_WIN64) && !defined(__clang__)
# pragma intrinsic(_BitScanReverse64)
......@@ -181,40 +177,88 @@ inline uint32_t clzll(uint64_t x) {
_BitScanReverse(&r, static_cast<uint32_t>(x));
# endif
assert(x != 0);
FMT_ASSERT(x != 0, "");
// Static analysis complains about using uninitialized data
// "r", but the only way that can happen is if "x" is 0,
// which the callers guarantee to not happen.
# pragma warning(suppress : 6102)
return 63 - r;
}
# define FMT_BUILTIN_CLZLL(n) fmt::internal::clzll(n)
# define FMT_BUILTIN_CLZLL(n) internal::clzll(n)
} // namespace internal
FMT_END_NAMESPACE
#endif
// Enable the deprecated numeric alignment.
#ifndef FMT_NUMERIC_ALIGN
# define FMT_NUMERIC_ALIGN 1
#endif
// Enable the deprecated percent specifier.
#ifndef FMT_DEPRECATED_PERCENT
# define FMT_DEPRECATED_PERCENT 0
#endif
FMT_BEGIN_NAMESPACE
namespace internal {
// A helper function to suppress bogus "conditional expression is constant"
// warnings.
template <typename T> inline T const_check(T value) { return value; }
// An equivalent of `*reinterpret_cast<Dest*>(&source)` that doesn't have
// undefined behavior (e.g. due to type aliasing).
// Example: uint64_t d = bit_cast<uint64_t>(2.718);
template <typename Dest, typename Source>
inline Dest bit_cast(const Source& source) {
static_assert(sizeof(Dest) == sizeof(Source), "size mismatch");
Dest dest;
std::memcpy(&dest, &source, sizeof(dest));
return dest;
}
inline bool is_big_endian() {
auto u = 1u;
struct bytes {
char data[sizeof(u)];
};
return bit_cast<bytes>(u).data[0] == 0;
}
// A fallback implementation of uintptr_t for systems that lack it.
struct fallback_uintptr {
unsigned char value[sizeof(void*)];
fallback_uintptr() = default;
explicit fallback_uintptr(const void* p) {
*this = bit_cast<fallback_uintptr>(p);
if (is_big_endian()) {
for (size_t i = 0, j = sizeof(void*) - 1; i < j; ++i, --j)
std::swap(value[i], value[j]);
}
}
};
#ifdef UINTPTR_MAX
using uintptr_t = ::uintptr_t;
inline uintptr_t to_uintptr(const void* p) { return bit_cast<uintptr_t>(p); }
#else
using uintptr_t = fallback_uintptr;
inline fallback_uintptr to_uintptr(const void* p) {
return fallback_uintptr(p);
}
#endif
// An equivalent of `*reinterpret_cast<Dest*>(&source)` that doesn't produce
// undefined behavior (e.g. due to type aliasing).
// Example: uint64_t d = bit_cast<uint64_t>(2.718);
template <typename Dest, typename Source>
inline Dest bit_cast(const Source& source) {
static_assert(sizeof(Dest) == sizeof(Source), "size mismatch");
Dest dest;
std::memcpy(&dest, &source, sizeof(dest));
return dest;
// Returns the largest possible value for type T. Same as
// std::numeric_limits<T>::max() but shorter and not affected by the max macro.
template <typename T> constexpr T max_value() {
return (std::numeric_limits<T>::max)();
}
template <typename T> constexpr int num_bits() {
return std::numeric_limits<T>::digits;
}
template <> constexpr int num_bits<fallback_uintptr>() {
return static_cast<int>(sizeof(void*) *
std::numeric_limits<unsigned char>::digits);
}
// An approximation of iterator_t for pre-C++20 systems.
......@@ -290,19 +334,21 @@ inline Iterator& reserve(Iterator& it, std::size_t) {
// An output iterator that counts the number of objects written to it and
// discards them.
template <typename T> class counting_iterator {
class counting_iterator {
private:
std::size_t count_;
mutable T blackhole_;
public:
using iterator_category = std::output_iterator_tag;
using value_type = T;
using difference_type = std::ptrdiff_t;
using pointer = T*;
using reference = T&;
using pointer = void;
using reference = void;
using _Unchecked_type = counting_iterator; // Mark iterator as checked.
struct value_type {
template <typename T> void operator=(const T&) {}
};
counting_iterator() : count_(0) {}
std::size_t count() const { return count_; }
......@@ -318,7 +364,7 @@ template <typename T> class counting_iterator {
return it;
}
T& operator*() const { return blackhole_; }
value_type operator*() const { return {}; }
};
template <typename OutputIt> class truncating_iterator_base {
......@@ -413,17 +459,6 @@ class output_range {
sentinel end() const { return {}; } // Sentinel is not used yet.
};
// A range with an iterator appending to a buffer.
template <typename T>
class buffer_range
: public output_range<std::back_insert_iterator<buffer<T>>, T> {
public:
using iterator = std::back_insert_iterator<buffer<T>>;
using output_range<iterator, T>::output_range;
buffer_range(buffer<T>& buf)
: output_range<iterator, T>(std::back_inserter(buf)) {}
};
template <typename Char>
inline size_t count_code_points(basic_string_view<Char> s) {
return s.size();
......@@ -439,6 +474,24 @@ inline size_t count_code_points(basic_string_view<char8_t> s) {
return num_code_points;
}
template <typename Char>
inline size_t code_point_index(basic_string_view<Char> s, size_t n) {
size_t size = s.size();
return n < size ? n : size;
}
// Calculates the index of the nth code point in a UTF-8 string.
inline size_t code_point_index(basic_string_view<char8_t> s, size_t n) {
const char8_t* data = s.data();
size_t num_code_points = 0;
for (size_t i = 0, size = s.size(); i != size; ++i) {
if ((data[i] & 0xc0) != 0x80 && ++num_code_points > n) {
return i;
}
}
return s.size();
}
inline char8_t to_char8_t(char c) { return static_cast<char8_t>(c); }
template <typename InputIt, typename OutChar>
......@@ -460,7 +513,7 @@ OutputIt copy_str(InputIt begin, InputIt end, OutputIt it) {
}
#ifndef FMT_USE_GRISU
# define FMT_USE_GRISU 0
# define FMT_USE_GRISU 1
#endif
template <typename T> constexpr bool use_grisu() {
......@@ -478,6 +531,17 @@ void buffer<T>::append(const U* begin, const U* end) {
}
} // namespace internal
// A range with an iterator appending to a buffer.
template <typename T>
class buffer_range : public internal::output_range<
std::back_insert_iterator<internal::buffer<T>>, T> {
public:
using iterator = std::back_insert_iterator<internal::buffer<T>>;
using internal::output_range<iterator, T>::output_range;
buffer_range(internal::buffer<T>& buf)
: internal::output_range<iterator, T>(std::back_inserter(buf)) {}
};
// A UTF-8 string view.
class u8string_view : public basic_string_view<char8_t> {
public:
......@@ -552,7 +616,7 @@ class basic_memory_buffer : private Allocator, public internal::buffer<T> {
: Allocator(alloc) {
this->set(store_, SIZE);
}
~basic_memory_buffer() { deallocate(); }
~basic_memory_buffer() FMT_OVERRIDE { deallocate(); }
private:
// Move data from other to this buffer.
......@@ -581,15 +645,15 @@ class basic_memory_buffer : private Allocator, public internal::buffer<T> {
of the other object to it.
\endrst
*/
basic_memory_buffer(basic_memory_buffer&& other) { move(other); }
basic_memory_buffer(basic_memory_buffer&& other) FMT_NOEXCEPT { move(other); }
/**
\rst
Moves the content of the other ``basic_memory_buffer`` object to this one.
\endrst
*/
basic_memory_buffer& operator=(basic_memory_buffer&& other) {
assert(this != &other);
basic_memory_buffer& operator=(basic_memory_buffer&& other) FMT_NOEXCEPT {
FMT_ASSERT(this != &other, "");
deallocate();
move(other);
return *this;
......@@ -628,7 +692,11 @@ class FMT_API format_error : public std::runtime_error {
explicit format_error(const char* message) : std::runtime_error(message) {}
explicit format_error(const std::string& message)
: std::runtime_error(message) {}
~format_error() FMT_NOEXCEPT;
format_error(const format_error&) = default;
format_error& operator=(const format_error&) = default;
format_error(format_error&&) = default;
format_error& operator=(format_error&&) = default;
~format_error() FMT_NOEXCEPT FMT_OVERRIDE;
};
namespace internal {
......@@ -644,11 +712,12 @@ FMT_CONSTEXPR bool is_negative(T) {
return false;
}
// Smallest of uint32_t and uint64_t that is large enough to represent all
// values of T.
// Smallest of uint32_t, uint64_t, uint128_t that is large enough to
// represent all values of T.
template <typename T>
using uint32_or_64_t =
conditional_t<std::numeric_limits<T>::digits <= 32, uint32_t, uint64_t>;
using uint32_or_64_or_128_t = conditional_t<
std::numeric_limits<T>::digits <= 32, uint32_t,
conditional_t<std::numeric_limits<T>::digits <= 64, uint64_t, uint128_t>>;
// Static data is placed in this class template for the header-only config.
template <typename T = void> struct FMT_EXTERN_TEMPLATE_API basic_data {
......@@ -663,6 +732,7 @@ template <typename T = void> struct FMT_EXTERN_TEMPLATE_API basic_data {
static const char background_color[];
static const char reset_color[5];
static const wchar_t wreset_color[5];
static const char signs[];
};
FMT_EXTERN template struct basic_data<void>;
......@@ -697,6 +767,23 @@ inline int count_digits(uint64_t n) {
}
#endif
#if FMT_USE_INT128
inline int count_digits(uint128_t n) {
int count = 1;
for (;;) {
// Integer division is slow so do it for a group of four digits instead
// of for every digit. The idea comes from the talk by Alexandrescu
// "Three Optimization Tips for C++". See speed-test for a comparison.
if (n < 10) return count;
if (n < 100) return count + 1;
if (n < 1000) return count + 2;
if (n < 10000) return count + 3;
n /= 10000U;
count += 4;
}
}
#endif
// Counts the number of digits in n. BITS = log2(radix).
template <unsigned BITS, typename UInt> inline int count_digits(UInt n) {
int num_digits = 0;
......@@ -708,17 +795,14 @@ template <unsigned BITS, typename UInt> inline int count_digits(UInt n) {
template <> int count_digits<4>(internal::fallback_uintptr n);
#if FMT_HAS_CPP_ATTRIBUTE(always_inline)
# define FMT_ALWAYS_INLINE __attribute__((always_inline))
#if FMT_GCC_VERSION || FMT_CLANG_VERSION
# define FMT_ALWAYS_INLINE inline __attribute__((always_inline))
#else
# define FMT_ALWAYS_INLINE
#endif
template <typename Handler>
inline char* lg(uint32_t n, Handler h) FMT_ALWAYS_INLINE;
// Computes g = floor(log10(n)) and calls h.on<g>(n);
template <typename Handler> inline char* lg(uint32_t n, Handler h) {
template <typename Handler> FMT_ALWAYS_INLINE char* lg(uint32_t n, Handler h) {
return n < 100 ? n < 10 ? h.template on<0>(n) : h.template on<1>(n)
: n < 1000000
? n < 10000 ? n < 1000 ? h.template on<2>(n)
......@@ -779,6 +863,14 @@ inline int count_digits(uint32_t n) {
}
#endif
template <typename Char> FMT_API std::string grouping_impl(locale_ref loc);
template <typename Char> inline std::string grouping(locale_ref loc) {
return grouping_impl<char>(loc);
}
template <> inline std::string grouping<wchar_t>(locale_ref loc) {
return grouping_impl<wchar_t>(loc);
}
template <typename Char> FMT_API Char thousands_sep_impl(locale_ref loc);
template <typename Char> inline Char thousands_sep(locale_ref loc) {
return Char(thousands_sep_impl<char>(loc));
......@@ -808,7 +900,7 @@ inline Char* format_decimal(Char* buffer, UInt value, int num_digits,
// Integer division is slow so do it for a group of two digits instead
// of for every digit. The idea comes from the talk by Alexandrescu
// "Three Optimization Tips for C++". See speed-test for a comparison.
unsigned index = static_cast<unsigned>((value % 100) * 2);
auto index = static_cast<unsigned>((value % 100) * 2);
value /= 100;
*--buffer = static_cast<Char>(data::digits[index + 1]);
add_thousands_sep(buffer);
......@@ -819,20 +911,26 @@ inline Char* format_decimal(Char* buffer, UInt value, int num_digits,
*--buffer = static_cast<Char>('0' + value);
return end;
}
unsigned index = static_cast<unsigned>(value * 2);
auto index = static_cast<unsigned>(value * 2);
*--buffer = static_cast<Char>(data::digits[index + 1]);
add_thousands_sep(buffer);
*--buffer = static_cast<Char>(data::digits[index]);
return end;
}
template <typename Int> constexpr int digits10() noexcept {
return std::numeric_limits<Int>::digits10;
}
template <> constexpr int digits10<int128_t>() noexcept { return 38; }
template <> constexpr int digits10<uint128_t>() noexcept { return 38; }
template <typename Char, typename UInt, typename Iterator, typename F>
inline Iterator format_decimal(Iterator out, UInt value, int num_digits,
F add_thousands_sep) {
FMT_ASSERT(num_digits >= 0, "invalid digit count");
// Buffer should be large enough to hold all digits (<= digits10 + 1).
enum { max_size = std::numeric_limits<UInt>::digits10 + 1 };
Char buffer[max_size + max_size / 3];
enum { max_size = digits10<UInt>() + 1 };
Char buffer[2 * max_size];
auto end = format_decimal(buffer, value, num_digits, add_thousands_sep);
return internal::copy_str<Char>(buffer, end, out);
}
......@@ -881,7 +979,7 @@ Char* format_uint(Char* buffer, internal::fallback_uintptr n, int num_digits,
template <unsigned BASE_BITS, typename Char, typename It, typename UInt>
inline It format_uint(It out, UInt value, int num_digits, bool upper = false) {
// Buffer should be large enough to hold all digits (digits / BASE_BITS + 1).
char buffer[std::numeric_limits<UInt>::digits / BASE_BITS + 1];
char buffer[num_bits<UInt>() / BASE_BITS + 1];
format_uint<BASE_BITS>(buffer, value, num_digits, upper);
return internal::copy_str<Char>(buffer, buffer + num_digits, out);
}
......@@ -929,9 +1027,8 @@ class utf16_to_utf8 {
FMT_API int convert(wstring_view s);
};
FMT_API void format_windows_error(fmt::internal::buffer<char>& out,
int error_code,
fmt::string_view message) FMT_NOEXCEPT;
FMT_API void format_windows_error(internal::buffer<char>& out, int error_code,
string_view message) FMT_NOEXCEPT;
#endif
template <typename T = void> struct null {};
......@@ -991,9 +1088,29 @@ using format_specs = basic_format_specs<char>;
namespace internal {
// A floating-point presentation format.
enum class float_format : unsigned char {
general, // General: exponent notation or fixed point based on magnitude.
exp, // Exponent notation with the default precision of 6, e.g. 1.2e-3.
fixed, // Fixed point with the default precision of 6, e.g. 0.0012.
hex
};
struct float_specs {
int precision;
float_format format : 8;
sign_t sign : 8;
bool upper : 1;
bool locale : 1;
bool percent : 1;
bool binary32 : 1;
bool use_grisu : 1;
bool trailing_zeros : 1;
};
// Writes the exponent exp in the form "[+-]d{2,3}" to buffer.
template <typename Char, typename It> It write_exponent(int exp, It it) {
FMT_ASSERT(-1000 < exp && exp < 1000, "exponent out of range");
FMT_ASSERT(-10000 < exp && exp < 10000, "exponent out of range");
if (exp < 0) {
*it++ = static_cast<Char>('-');
exp = -exp;
......@@ -1001,7 +1118,9 @@ template <typename Char, typename It> It write_exponent(int exp, It it) {
*it++ = static_cast<Char>('+');
}
if (exp >= 100) {
*it++ = static_cast<Char>(static_cast<char>('0' + exp / 100));
const char* top = data::digits + (exp / 100) * 2;
if (exp >= 1000) *it++ = static_cast<Char>(top[0]);
*it++ = static_cast<Char>(top[1]);
exp %= 100;
}
const char* d = data::digits + exp * 2;
......@@ -1010,103 +1129,121 @@ template <typename Char, typename It> It write_exponent(int exp, It it) {
return it;
}
struct gen_digits_params {
int num_digits;
bool fixed;
bool upper;
bool trailing_zeros;
};
// The number is given as v = digits * pow(10, exp).
template <typename Char, typename It>
It grisu_prettify(const char* digits, int size, int exp, It it,
gen_digits_params params, Char decimal_point) {
// pow(10, full_exp - 1) <= v <= pow(10, full_exp).
int full_exp = size + exp;
if (!params.fixed) {
// Insert a decimal point after the first digit and add an exponent.
*it++ = static_cast<Char>(*digits);
if (size > 1) *it++ = decimal_point;
exp += size - 1;
it = copy_str<Char>(digits + 1, digits + size, it);
if (size < params.num_digits)
it = std::fill_n(it, params.num_digits - size, static_cast<Char>('0'));
*it++ = static_cast<Char>(params.upper ? 'E' : 'e');
return write_exponent<Char>(exp, it);
}
if (size <= full_exp) {
// 1234e7 -> 12340000000[.0+]
it = copy_str<Char>(digits, digits + size, it);
it = std::fill_n(it, full_exp - size, static_cast<Char>('0'));
int num_zeros = (std::max)(params.num_digits - full_exp, 1);
if (params.trailing_zeros) {
*it++ = decimal_point;
template <typename Char> class float_writer {
private:
// The number is given as v = digits_ * pow(10, exp_).
const char* digits_;
int num_digits_;
int exp_;
size_t size_;
float_specs specs_;
Char decimal_point_;
template <typename It> It prettify(It it) const {
// pow(10, full_exp - 1) <= v <= pow(10, full_exp).
int full_exp = num_digits_ + exp_;
if (specs_.format == float_format::exp) {
// Insert a decimal point after the first digit and add an exponent.
*it++ = static_cast<Char>(*digits_);
if (num_digits_ > 1) *it++ = decimal_point_;
it = copy_str<Char>(digits_ + 1, digits_ + num_digits_, it);
int num_zeros = specs_.precision - num_digits_;
if (num_zeros > 0 && specs_.trailing_zeros)
it = std::fill_n(it, num_zeros, static_cast<Char>('0'));
*it++ = static_cast<Char>(specs_.upper ? 'E' : 'e');
return write_exponent<Char>(full_exp - 1, it);
}
if (num_digits_ <= full_exp) {
// 1234e7 -> 12340000000[.0+]
it = copy_str<Char>(digits_, digits_ + num_digits_, it);
it = std::fill_n(it, full_exp - num_digits_, static_cast<Char>('0'));
if (specs_.trailing_zeros) {
*it++ = decimal_point_;
int num_zeros = specs_.precision - full_exp;
if (num_zeros <= 0) {
if (specs_.format != float_format::fixed)
*it++ = static_cast<Char>('0');
return it;
}
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
if (num_zeros > 1000)
throw std::runtime_error("fuzz mode - avoiding excessive cpu use");
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'));
}
} else if (full_exp > 0) {
// 1234e-2 -> 12.34[0+]
it = copy_str<Char>(digits, digits + full_exp, it);
if (!params.trailing_zeros) {
// Remove trailing zeros.
while (size > full_exp && digits[size - 1] == '0') --size;
if (size != full_exp) *it++ = decimal_point;
return copy_str<Char>(digits + full_exp, digits + size, it);
}
*it++ = decimal_point;
it = copy_str<Char>(digits + full_exp, digits + size, it);
if (params.num_digits > size) {
// Add trailing zeros.
int num_zeros = params.num_digits - size;
it = std::fill_n(it, num_zeros, static_cast<Char>('0'));
}
} else {
// 1234e-6 -> 0.001234
*it++ = static_cast<Char>('0');
int num_zeros = -full_exp;
if (params.num_digits >= 0 && params.num_digits < num_zeros)
num_zeros = params.num_digits;
if (!params.trailing_zeros)
while (size > 0 && digits[size - 1] == '0') --size;
if (num_zeros != 0 || size != 0) {
*it++ = decimal_point;
it = std::fill_n(it, num_zeros, static_cast<Char>('0'));
it = copy_str<Char>(digits, digits + size, it);
it = std::fill_n(it, num_zeros, static_cast<Char>('0'));
}
} else if (full_exp > 0) {
// 1234e-2 -> 12.34[0+]
it = copy_str<Char>(digits_, digits_ + full_exp, it);
if (!specs_.trailing_zeros) {
// Remove trailing zeros.
int num_digits = num_digits_;
while (num_digits > full_exp && digits_[num_digits - 1] == '0')
--num_digits;
if (num_digits != full_exp) *it++ = decimal_point_;
return copy_str<Char>(digits_ + full_exp, digits_ + num_digits, it);
}
*it++ = decimal_point_;
it = copy_str<Char>(digits_ + full_exp, digits_ + num_digits_, it);
if (specs_.precision > num_digits_) {
// Add trailing zeros.
int num_zeros = specs_.precision - num_digits_;
it = std::fill_n(it, num_zeros, static_cast<Char>('0'));
}
} else {
// 1234e-6 -> 0.001234
*it++ = static_cast<Char>('0');
int num_zeros = -full_exp;
if (specs_.precision >= 0 && specs_.precision < num_zeros)
num_zeros = specs_.precision;
int num_digits = num_digits_;
if (!specs_.trailing_zeros)
while (num_digits > 0 && digits_[num_digits - 1] == '0') --num_digits;
if (num_zeros != 0 || num_digits != 0) {
*it++ = decimal_point_;
it = std::fill_n(it, num_zeros, static_cast<Char>('0'));
it = copy_str<Char>(digits_, digits_ + num_digits, it);
}
}
return it;
}
return it;
}
namespace grisu_options {
enum { fixed = 1, grisu3 = 2 };
}
public:
float_writer(const char* digits, int num_digits, int exp, float_specs specs,
Char decimal_point)
: digits_(digits),
num_digits_(num_digits),
exp_(exp),
specs_(specs),
decimal_point_(decimal_point) {
int full_exp = num_digits + exp - 1;
int precision = specs.precision > 0 ? specs.precision : 16;
if (specs_.format == float_format::general &&
!(full_exp >= -4 && full_exp < precision)) {
specs_.format = float_format::exp;
}
size_ = prettify(counting_iterator()).count();
size_ += specs.sign ? 1 : 0;
}
// Formats value using the Grisu algorithm:
// https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf
template <typename Double, FMT_ENABLE_IF(sizeof(Double) == sizeof(uint64_t))>
FMT_API bool grisu_format(Double, buffer<char>&, int, unsigned, int&);
template <typename Double, FMT_ENABLE_IF(sizeof(Double) != sizeof(uint64_t))>
inline bool grisu_format(Double, buffer<char>&, int, unsigned, int&) {
return false;
}
size_t size() const { return size_; }
size_t width() const { return size(); }
struct sprintf_specs {
int precision;
char type;
bool alt : 1;
template <typename It> void operator()(It&& it) {
if (specs_.sign) *it++ = static_cast<Char>(data::signs[specs_.sign]);
it = prettify(it);
}
};
template <typename Char>
constexpr sprintf_specs(basic_format_specs<Char> specs)
: precision(specs.precision), type(specs.type), alt(specs.alt) {}
template <typename T>
int format_float(T value, int precision, float_specs specs, buffer<char>& buf);
constexpr bool has_precision() const { return precision >= 0; }
};
// Formats a floating-point number with snprintf.
template <typename T>
int snprintf_float(T value, int precision, float_specs specs,
buffer<char>& buf);
template <typename Double>
char* sprintf_format(Double, internal::buffer<char>&, sprintf_specs);
template <typename T> T promote_float(T value) { return value; }
inline double promote_float(float value) { return value; }
template <typename Handler>
FMT_CONSTEXPR void handle_int_type_spec(char spec, Handler&& handler) {
......@@ -1134,36 +1271,56 @@ FMT_CONSTEXPR void handle_int_type_spec(char spec, Handler&& handler) {
}
}
template <typename Handler>
FMT_CONSTEXPR void handle_float_type_spec(char spec, Handler&& handler) {
switch (spec) {
template <typename ErrorHandler = error_handler, typename Char>
FMT_CONSTEXPR float_specs parse_float_type_spec(
const basic_format_specs<Char>& specs, ErrorHandler&& eh = {}) {
auto result = float_specs();
result.trailing_zeros = specs.alt;
switch (specs.type) {
case 0:
case 'g':
result.format = float_format::general;
result.trailing_zeros |= specs.precision != 0;
break;
case 'G':
handler.on_general();
result.upper = true;
FMT_FALLTHROUGH;
case 'g':
result.format = float_format::general;
break;
case 'e':
case 'E':
handler.on_exp();
result.upper = true;
FMT_FALLTHROUGH;
case 'e':
result.format = float_format::exp;
result.trailing_zeros |= specs.precision != 0;
break;
case 'f':
case 'F':
handler.on_fixed();
result.upper = true;
FMT_FALLTHROUGH;
case 'f':
result.format = float_format::fixed;
result.trailing_zeros |= specs.precision != 0;
break;
#if FMT_DEPRECATED_PERCENT
case '%':
handler.on_percent();
result.format = float_format::fixed;
result.percent = true;
break;
case 'a':
#endif
case 'A':
handler.on_hex();
result.upper = true;
FMT_FALLTHROUGH;
case 'a':
result.format = float_format::hex;
break;
case 'n':
handler.on_num();
result.locale = true;
break;
default:
handler.on_error();
eh.on_error("invalid type specifier");
break;
}
return result;
}
template <typename Char, typename Handler>
......@@ -1211,24 +1368,6 @@ template <typename ErrorHandler> class int_type_checker : private ErrorHandler {
}
};
template <typename ErrorHandler>
class float_type_checker : private ErrorHandler {
public:
FMT_CONSTEXPR explicit float_type_checker(ErrorHandler eh)
: ErrorHandler(eh) {}
FMT_CONSTEXPR void on_general() {}
FMT_CONSTEXPR void on_exp() {}
FMT_CONSTEXPR void on_fixed() {}
FMT_CONSTEXPR void on_percent() {}
FMT_CONSTEXPR void on_hex() {}
FMT_CONSTEXPR void on_num() {}
FMT_CONSTEXPR void on_error() {
ErrorHandler::on_error("invalid type specifier");
}
};
template <typename ErrorHandler>
class char_specs_checker : public ErrorHandler {
private:
......@@ -1271,6 +1410,20 @@ void arg_map<Context>::init(const basic_format_args<Context>& args) {
}
}
template <typename Char> struct nonfinite_writer {
sign_t sign;
const char* str;
static constexpr size_t str_size = 3;
size_t size() const { return str_size + (sign ? 1 : 0); }
size_t width() const { return size(); }
template <typename It> void operator()(It&& it) const {
if (sign) *it++ = static_cast<Char>(data::signs[sign]);
it = copy_str<Char>(str, str + str_size, it);
}
};
// This template provides operations for formatting and writing data into a
// character range.
template <typename Range> class basic_writer {
......@@ -1281,7 +1434,7 @@ template <typename Range> class basic_writer {
private:
iterator out_; // Output iterator.
internal::locale_ref locale_;
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_.
......@@ -1301,7 +1454,7 @@ template <typename Range> class basic_writer {
template <typename It> void operator()(It&& it) const {
if (prefix.size() != 0)
it = internal::copy_str<char_type>(prefix.begin(), prefix.end(), it);
it = copy_str<char_type>(prefix.begin(), prefix.end(), it);
it = std::fill_n(it, padding, fill);
f(it);
}
......@@ -1312,18 +1465,18 @@ template <typename Range> class basic_writer {
// where <digits> are written by f(it).
template <typename F>
void write_int(int num_digits, string_view prefix, format_specs specs, F f) {
std::size_t size = prefix.size() + internal::to_unsigned(num_digits);
std::size_t size = prefix.size() + to_unsigned(num_digits);
char_type fill = specs.fill[0];
std::size_t padding = 0;
if (specs.align == align::numeric) {
auto unsiged_width = internal::to_unsigned(specs.width);
auto unsiged_width = to_unsigned(specs.width);
if (unsiged_width > size) {
padding = unsiged_width - size;
size = unsiged_width;
}
} else if (specs.precision > num_digits) {
size = prefix.size() + internal::to_unsigned(specs.precision);
padding = internal::to_unsigned(specs.precision - num_digits);
size = prefix.size() + to_unsigned(specs.precision);
padding = to_unsigned(specs.precision - num_digits);
fill = static_cast<char_type>('0');
}
if (specs.align == align::none) specs.align = align::right;
......@@ -1332,19 +1485,19 @@ template <typename Range> class basic_writer {
// Writes a decimal integer.
template <typename Int> void write_decimal(Int value) {
auto abs_value = static_cast<uint32_or_64_t<Int>>(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);
auto abs_value = static_cast<uint32_or_64_or_128_t<Int>>(value);
bool negative = is_negative(value);
// Don't do -abs_value since it trips unsigned-integer-overflow sanitizer.
if (negative) abs_value = ~abs_value + 1;
int num_digits = count_digits(abs_value);
auto&& it = reserve((negative ? 1 : 0) + static_cast<size_t>(num_digits));
if (negative) *it++ = static_cast<char_type>('-');
it = format_decimal<char_type>(it, abs_value, num_digits);
}
// The handle_int_type_spec handler that writes an integer.
template <typename Int, typename Specs> struct int_writer {
using unsigned_type = uint32_or_64_t<Int>;
using unsigned_type = uint32_or_64_or_128_t<Int>;
basic_writer<Range>& writer;
const Specs& specs;
......@@ -1359,7 +1512,7 @@ template <typename Range> class basic_writer {
specs(s),
abs_value(static_cast<unsigned_type>(value)),
prefix_size(0) {
if (internal::is_negative(value)) {
if (is_negative(value)) {
prefix[0] = '-';
++prefix_size;
abs_value = 0 - abs_value;
......@@ -1379,7 +1532,7 @@ template <typename Range> class basic_writer {
};
void on_dec() {
int num_digits = internal::count_digits(abs_value);
int num_digits = count_digits(abs_value);
writer.write_int(num_digits, get_prefix(), specs,
dec_writer{abs_value, num_digits});
}
......@@ -1389,8 +1542,8 @@ template <typename Range> class basic_writer {
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.specs.type != 'x');
it = format_uint<4, char_type>(it, self.abs_value, num_digits,
self.specs.type != 'x');
}
};
......@@ -1399,7 +1552,7 @@ template <typename Range> class basic_writer {
prefix[prefix_size++] = '0';
prefix[prefix_size++] = specs.type;
}
int num_digits = internal::count_digits<4>(abs_value);
int num_digits = count_digits<4>(abs_value);
writer.write_int(num_digits, get_prefix(), specs,
hex_writer{*this, num_digits});
}
......@@ -1409,7 +1562,7 @@ template <typename Range> class basic_writer {
int num_digits;
template <typename It> void operator()(It&& it) const {
it = internal::format_uint<BITS, char_type>(it, abs_value, num_digits);
it = format_uint<BITS, char_type>(it, abs_value, num_digits);
}
};
......@@ -1418,14 +1571,14 @@ template <typename Range> class basic_writer {
prefix[prefix_size++] = '0';
prefix[prefix_size++] = static_cast<char>(specs.type);
}
int num_digits = internal::count_digits<1>(abs_value);
int num_digits = count_digits<1>(abs_value);
writer.write_int(num_digits, get_prefix(), specs,
bin_writer<1>{abs_value, num_digits});
}
void on_oct() {
int num_digits = internal::count_digits<3>(abs_value);
if (specs.alt && specs.precision <= num_digits) {
int num_digits = count_digits<3>(abs_value);
if (specs.alt && specs.precision <= num_digits && abs_value != 0) {
// 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';
......@@ -1439,30 +1592,50 @@ template <typename Range> class basic_writer {
struct num_writer {
unsigned_type abs_value;
int size;
const std::string& groups;
char_type sep;
template <typename It> void operator()(It&& it) const {
basic_string_view<char_type> s(&sep, sep_size);
// Index of a decimal digit with the least significant digit having
// index 0.
unsigned digit_index = 0;
it = internal::format_decimal<char_type>(
it, abs_value, size, [s, &digit_index](char_type*& buffer) {
if (++digit_index % 3 != 0) return;
int digit_index = 0;
std::string::const_iterator group = groups.cbegin();
it = format_decimal<char_type>(
it, abs_value, size,
[this, s, &group, &digit_index](char_type*& buffer) {
if (*group <= 0 || ++digit_index % *group != 0 ||
*group == max_value<char>())
return;
if (group + 1 != groups.cend()) {
digit_index = 0;
++group;
}
buffer -= s.size();
std::uninitialized_copy(s.data(), s.data() + s.size(),
internal::make_checked(buffer, s.size()));
make_checked(buffer, s.size()));
});
}
};
void on_num() {
char_type sep = internal::thousands_sep<char_type>(writer.locale_);
std::string groups = grouping<char_type>(writer.locale_);
if (groups.empty()) return on_dec();
auto sep = thousands_sep<char_type>(writer.locale_);
if (!sep) return on_dec();
int num_digits = internal::count_digits(abs_value);
int size = num_digits + sep_size * ((num_digits - 1) / 3);
int num_digits = count_digits(abs_value);
int size = num_digits;
std::string::const_iterator group = groups.cbegin();
while (group != groups.cend() && num_digits > *group && *group > 0 &&
*group != max_value<char>()) {
size += sep_size;
num_digits -= *group;
++group;
}
if (group == groups.cend())
size += sep_size * ((num_digits - 1) / groups.back());
writer.write_int(size, get_prefix(), specs,
num_writer{abs_value, size, sep});
num_writer{abs_value, size, groups, sep});
}
FMT_NORETURN void on_error() {
......@@ -1470,98 +1643,17 @@ template <typename Range> class basic_writer {
}
};
enum { inf_size = 3 }; // This is an enum to workaround a bug in MSVC.
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));
}
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>('%');
}
};
struct double_writer {
char sign;
internal::buffer<char>& buffer;
char* decimal_point_pos;
char_type decimal_point;
size_t size() const { return buffer.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);
auto begin = buffer.begin();
if (decimal_point_pos) {
it = internal::copy_str<char_type>(begin, decimal_point_pos, it);
*it++ = decimal_point;
begin = decimal_point_pos + 1;
}
it = internal::copy_str<char_type>(begin, buffer.end(), it);
}
};
class grisu_writer {
private:
internal::buffer<char>& digits_;
size_t size_;
char sign_;
int exp_;
internal::gen_digits_params params_;
char_type decimal_point_;
public:
grisu_writer(char sign, internal::buffer<char>& digits, int exp,
const internal::gen_digits_params& params,
char_type decimal_point)
: digits_(digits),
sign_(sign),
exp_(exp),
params_(params),
decimal_point_(decimal_point) {
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_, decimal_point_);
}
};
template <typename Char> struct str_writer {
const Char* s;
size_t size_;
size_t size() const { return size_; }
size_t width() const {
return internal::count_code_points(basic_string_view<Char>(s, size_));
return count_code_points(basic_string_view<Char>(s, size_));
}
template <typename It> void operator()(It&& it) const {
it = internal::copy_str<char_type>(s, s + size_, it);
it = copy_str<char_type>(s, s + size_, it);
}
};
......@@ -1575,14 +1667,12 @@ template <typename Range> class basic_writer {
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);
it = format_uint<4, char_type>(it, value, num_digits);
}
};
public:
/** Constructs a ``basic_writer`` object. */
explicit basic_writer(Range out,
internal::locale_ref loc = internal::locale_ref())
explicit basic_writer(Range out, locale_ref loc = locale_ref())
: out_(out.begin()), locale_(loc) {}
iterator out() const { return out_; }
......@@ -1621,32 +1711,70 @@ template <typename Range> class basic_writer {
void write(unsigned long value) { write_decimal(value); }
void write(unsigned long long value) { write_decimal(value); }
// Writes a formatted integer.
#if FMT_USE_INT128
void write(int128_t value) { write_decimal(value); }
void write(uint128_t value) { write_decimal(value); }
#endif
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));
handle_int_type_spec(spec.type, int_writer<T, Spec>(*this, value, spec));
}
void write(double value, const format_specs& specs = format_specs()) {
write_double(value, specs);
}
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
void write(T value, format_specs specs = {}) {
float_specs fspecs = parse_float_type_spec(specs);
fspecs.sign = specs.sign;
if (std::signbit(value)) { // value < 0 is false for NaN so use signbit.
fspecs.sign = sign::minus;
value = -value;
} else if (fspecs.sign == sign::minus) {
fspecs.sign = sign::none;
}
/**
\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& specs = format_specs()) {
write_double(value, specs);
}
if (!std::isfinite(value)) {
auto str = std::isinf(value) ? (fspecs.upper ? "INF" : "inf")
: (fspecs.upper ? "NAN" : "nan");
return write_padded(specs, nonfinite_writer<char_type>{fspecs.sign, str});
}
if (specs.align == align::none) {
specs.align = align::right;
} else if (specs.align == align::numeric) {
if (fspecs.sign) {
auto&& it = reserve(1);
*it++ = static_cast<char_type>(data::signs[fspecs.sign]);
fspecs.sign = sign::none;
if (specs.width != 0) --specs.width;
}
specs.align = align::right;
}
// Formats a floating-point number (double or long double).
template <typename T, bool USE_GRISU = fmt::internal::use_grisu<T>()>
void write_double(T value, const format_specs& specs);
memory_buffer buffer;
if (fspecs.format == float_format::hex) {
if (fspecs.sign) buffer.push_back(data::signs[fspecs.sign]);
snprintf_float(promote_float(value), specs.precision, fspecs, buffer);
write_padded(specs, str_writer<char>{buffer.data(), buffer.size()});
return;
}
int precision = specs.precision >= 0 || !specs.type ? specs.precision : 6;
if (fspecs.format == float_format::exp) ++precision;
if (const_check(std::is_same<T, float>())) fspecs.binary32 = true;
fspecs.use_grisu = use_grisu<T>();
if (const_check(FMT_DEPRECATED_PERCENT) && fspecs.percent) value *= 100;
int exp = format_float(promote_float(value), precision, fspecs, buffer);
if (const_check(FMT_DEPRECATED_PERCENT) && fspecs.percent) {
buffer.push_back('%');
--exp; // Adjust decimal place position.
}
fspecs.precision = precision;
char_type point = fspecs.locale ? decimal_point<char_type>(locale_)
: static_cast<char_type>('.');
write_padded(specs, float_writer<char_type>(buffer.data(),
static_cast<int>(buffer.size()),
exp, fspecs, point));
}
/** Writes a character to the buffer. */
void write(char value) {
auto&& it = reserve(1);
*it++ = value;
......@@ -1658,14 +1786,9 @@ template <typename Range> class basic_writer {
*it++ = value;
}
/**
\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);
it = copy_str<char_type>(value.begin(), value.end(), it);
}
void write(wstring_view value) {
static_assert(std::is_same<char_type, wchar_t>::value, "");
......@@ -1673,25 +1796,23 @@ template <typename Range> class basic_writer {
it = std::copy(value.begin(), value.end(), it);
}
// Writes a formatted string.
template <typename Char>
void write(const Char* s, std::size_t size, const format_specs& specs) {
write_padded(specs, str_writer<Char>{s, size});
}
template <typename Char>
void write(basic_string_view<Char> s,
const format_specs& specs = format_specs()) {
void write(basic_string_view<Char> s, const format_specs& specs = {}) {
const Char* data = s.data();
std::size_t size = s.size();
if (specs.precision >= 0 && internal::to_unsigned(specs.precision) < size)
size = internal::to_unsigned(specs.precision);
if (specs.precision >= 0 && to_unsigned(specs.precision) < size)
size = code_point_index(s, to_unsigned(specs.precision));
write(data, size, specs);
}
template <typename UIntPtr>
void write_pointer(UIntPtr value, const format_specs* specs) {
int num_digits = internal::count_digits<4>(value);
int num_digits = count_digits<4>(value);
auto pw = pointer_writer<UIntPtr>{value, num_digits};
if (!specs) return pw(reserve(to_unsigned(num_digits) + 2));
format_specs specs_copy = *specs;
......@@ -1702,6 +1823,10 @@ template <typename Range> class basic_writer {
using writer = basic_writer<buffer_range<char>>;
template <typename T> struct is_integral : std::is_integral<T> {};
template <> struct is_integral<int128_t> : std::true_type {};
template <> struct is_integral<uint128_t> : std::true_type {};
template <typename Range, typename ErrorHandler = internal::error_handler>
class arg_formatter_base {
public:
......@@ -1731,7 +1856,7 @@ class arg_formatter_base {
}
void write_pointer(const void* p) {
writer_.write_pointer(internal::bit_cast<internal::uintptr_t>(p), specs_);
writer_.write_pointer(internal::to_uintptr(p), specs_);
}
protected:
......@@ -1764,7 +1889,7 @@ class arg_formatter_base {
return out();
}
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
template <typename T, FMT_ENABLE_IF(is_integral<T>::value)>
iterator operator()(T value) {
if (specs_)
writer_.write_int(value, *specs_);
......@@ -1787,7 +1912,7 @@ class arg_formatter_base {
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
iterator operator()(T value) {
writer_.write_double(value, specs_ ? *specs_ : format_specs());
writer_.write(value, specs_ ? *specs_ : format_specs());
return out();
}
......@@ -1852,14 +1977,14 @@ template <typename Char> FMT_CONSTEXPR bool is_name_start(Char c) {
template <typename Char, typename ErrorHandler>
FMT_CONSTEXPR int parse_nonnegative_int(const Char*& begin, const Char* end,
ErrorHandler&& eh) {
assert(begin != end && '0' <= *begin && *begin <= '9');
FMT_ASSERT(begin != end && '0' <= *begin && *begin <= '9', "");
if (*begin == '0') {
++begin;
return 0;
}
unsigned value = 0;
// Convert to unsigned to prevent a warning.
constexpr unsigned max_int = (std::numeric_limits<int>::max)();
constexpr unsigned max_int = max_value<int>();
unsigned big = max_int / 10;
do {
// Check for overflow.
......@@ -1878,11 +2003,11 @@ template <typename Context> class custom_formatter {
private:
using char_type = typename Context::char_type;
basic_parse_context<char_type>& parse_ctx_;
basic_format_parse_context<char_type>& parse_ctx_;
Context& ctx_;
public:
explicit custom_formatter(basic_parse_context<char_type>& parse_ctx,
explicit custom_formatter(basic_format_parse_context<char_type>& parse_ctx,
Context& ctx)
: parse_ctx_(parse_ctx), ctx_(ctx) {}
......@@ -1896,7 +2021,7 @@ template <typename Context> class custom_formatter {
template <typename T>
using is_integer =
bool_constant<std::is_integral<T>::value && !std::is_same<T, bool>::value &&
bool_constant<is_integral<T>::value && !std::is_same<T, bool>::value &&
!std::is_same<T, char>::value &&
!std::is_same<T, wchar_t>::value>;
......@@ -1981,20 +2106,20 @@ template <typename ErrorHandler> class numeric_specs_checker {
: error_handler_(eh), arg_type_(arg_type) {}
FMT_CONSTEXPR void require_numeric_argument() {
if (!is_arithmetic(arg_type_))
if (!is_arithmetic_type(arg_type_))
error_handler_.on_error("format specifier requires numeric argument");
}
FMT_CONSTEXPR void check_sign() {
require_numeric_argument();
if (is_integral(arg_type_) && arg_type_ != int_type &&
if (is_integral_type(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 check_precision() {
if (is_integral(arg_type_) || arg_type_ == internal::pointer_type)
if (is_integral_type(arg_type_) || arg_type_ == internal::pointer_type)
error_handler_.on_error("precision not allowed for this argument type");
}
......@@ -2049,14 +2174,12 @@ template <typename Handler> class specs_checker : public Handler {
numeric_specs_checker<Handler> checker_;
};
template <template <typename> class Handler, typename T, typename FormatArg,
template <template <typename> class Handler, 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);
FMT_CONSTEXPR int get_dynamic_spec(FormatArg arg, ErrorHandler eh) {
unsigned long long value = visit_format_arg(Handler<ErrorHandler>(eh), arg);
if (value > to_unsigned(max_value<int>())) eh.on_error("number is too big");
return static_cast<int>(value);
}
struct auto_id {};
......@@ -2081,13 +2204,13 @@ class specs_handler : public specs_setter<typename Context::char_type> {
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());
this->specs_.width = get_dynamic_spec<width_checker>(
get_arg(arg_id), context_.error_handler());
}
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());
this->specs_.precision = get_dynamic_spec<precision_checker>(
get_arg(arg_id), context_.error_handler());
}
void on_error(const char* message) { context_.on_error(message); }
......@@ -2114,24 +2237,6 @@ class specs_handler : public specs_setter<typename Context::char_type> {
Context& context_;
};
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_(to_unsigned(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_};
}
std::size_t offset_;
std::size_t size_;
};
enum class arg_id_kind { none, index, name };
// An argument reference.
......@@ -2139,7 +2244,7 @@ template <typename Char> struct arg_ref {
FMT_CONSTEXPR arg_ref() : kind(arg_id_kind::none), val() {}
FMT_CONSTEXPR explicit arg_ref(int index)
: kind(arg_id_kind::index), val(index) {}
FMT_CONSTEXPR explicit arg_ref(string_view_metadata name)
FMT_CONSTEXPR explicit arg_ref(basic_string_view<Char> name)
: kind(arg_id_kind::name), val(name) {}
FMT_CONSTEXPR arg_ref& operator=(int idx) {
......@@ -2150,12 +2255,11 @@ template <typename Char> struct arg_ref {
arg_id_kind kind;
union value {
FMT_CONSTEXPR value() : index(0u) {}
FMT_CONSTEXPR value(int id) : index(id) {}
FMT_CONSTEXPR value(string_view_metadata n) : name(n) {}
FMT_CONSTEXPR value(int id = 0) : index{id} {}
FMT_CONSTEXPR value(basic_string_view<Char> n) : name(n) {}
int index;
string_view_metadata name;
basic_string_view<Char> name;
} val;
};
......@@ -2213,8 +2317,7 @@ class dynamic_specs_handler
context_.check_arg_id(arg_id);
basic_string_view<char_type> format_str(
context_.begin(), to_unsigned(context_.end() - context_.begin()));
const auto id_metadata = string_view_metadata(format_str, arg_id);
return arg_ref_type(id_metadata);
return arg_ref_type(arg_id);
}
dynamic_format_specs<char_type>& specs_;
......@@ -2224,18 +2327,24 @@ class dynamic_specs_handler
template <typename Char, typename IDHandler>
FMT_CONSTEXPR const Char* parse_arg_id(const Char* begin, const Char* end,
IDHandler&& handler) {
assert(begin != end);
FMT_ASSERT(begin != end, "");
Char c = *begin;
if (c == '}' || c == ':') return handler(), begin;
if (c == '}' || c == ':') {
handler();
return begin;
}
if (c >= '0' && c <= '9') {
int index = parse_nonnegative_int(begin, end, handler);
if (begin == end || (*begin != '}' && *begin != ':'))
return handler.on_error("invalid format string"), begin;
handler(index);
handler.on_error("invalid format string");
else
handler(index);
return begin;
}
if (!is_name_start(c)) {
handler.on_error("invalid format string");
return begin;
}
if (!is_name_start(c))
return handler.on_error("invalid format string"), begin;
auto it = begin;
do {
++it;
......@@ -2294,9 +2403,11 @@ FMT_CONSTEXPR const Char* parse_align(const Char* begin, const Char* end,
case '>':
align = align::right;
break;
#if FMT_NUMERIC_ALIGN
case '=':
align = align::numeric;
break;
#endif
case '^':
align = align::center;
break;
......@@ -2439,7 +2550,7 @@ template <typename Handler, typename Char> struct id_adapter {
template <bool IS_CONSTEXPR, typename Char, typename Handler>
FMT_CONSTEXPR void parse_format_string(basic_string_view<Char> format_str,
Handler&& handler) {
struct writer {
struct pfs_writer {
FMT_CONSTEXPR void operator()(const Char* begin, const Char* end) {
if (begin == end) return;
for (;;) {
......@@ -2497,10 +2608,9 @@ FMT_CONSTEXPR const typename ParseContext::char_type* parse_format_specs(
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;
auto f = conditional_t<has_formatter<mapped_type, context>::value,
formatter<mapped_type, char_type>,
internal::fallback_formatter<T, char_type>>();
return f.parse(ctx);
}
......@@ -2509,7 +2619,7 @@ 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)()),
: arg_id_(max_value<unsigned>()),
context_(format_str, eh),
parse_funcs_{&parse_format_specs<Args, parse_context_type>...} {}
......@@ -2540,7 +2650,7 @@ class format_string_checker {
}
private:
using parse_context_type = basic_parse_context<Char, ErrorHandler>;
using parse_context_type = basic_format_parse_context<Char, ErrorHandler>;
enum { num_args = sizeof...(Args) };
FMT_CONSTEXPR void check_arg_id() {
......@@ -2573,32 +2683,29 @@ void check_format_string(S format_str) {
(void)invalid_format;
}
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) {
template <template <typename> class Handler, typename Context>
void handle_dynamic_spec(int& value, arg_ref<typename Context::char_type> ref,
Context& ctx) {
switch (ref.kind) {
case arg_id_kind::none:
break;
case arg_id_kind::index:
internal::set_dynamic_spec<Handler>(value, ctx.arg(ref.val.index),
ctx.error_handler());
value = internal::get_dynamic_spec<Handler>(ctx.arg(ref.val.index),
ctx.error_handler());
break;
case arg_id_kind::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());
case arg_id_kind::name:
value = internal::get_dynamic_spec<Handler>(ctx.arg(ref.val.name),
ctx.error_handler());
break;
}
}
}
} // namespace internal
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>>;
using basic_writer FMT_DEPRECATED_ALIAS = internal::basic_writer<Range>;
using writer FMT_DEPRECATED_ALIAS = internal::writer;
using wwriter FMT_DEPRECATED_ALIAS =
internal::basic_writer<buffer_range<wchar_t>>;
/** The default argument formatter. */
template <typename Range>
......@@ -2609,7 +2716,7 @@ class arg_formatter : public internal::arg_formatter_base<Range> {
using context_type = basic_format_context<typename base::iterator, char_type>;
context_type& ctx_;
basic_parse_context<char_type>* parse_ctx_;
basic_format_parse_context<char_type>* parse_ctx_;
public:
using range = Range;
......@@ -2623,9 +2730,10 @@ class arg_formatter : public internal::arg_formatter_base<Range> {
*specs* 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* specs = nullptr)
explicit arg_formatter(
context_type& ctx,
basic_format_parse_context<char_type>* parse_ctx = nullptr,
format_specs* specs = nullptr)
: base(Range(ctx.out()), specs, ctx.locale()),
ctx_(ctx),
parse_ctx_(parse_ctx) {}
......@@ -2635,7 +2743,7 @@ class arg_formatter : public internal::arg_formatter_base<Range> {
/** 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();
return ctx_.out();
}
};
......@@ -2676,7 +2784,11 @@ class FMT_API system_error : public std::runtime_error {
: std::runtime_error("") {
init(error_code, message, make_format_args(args...));
}
~system_error() FMT_NOEXCEPT;
system_error(const system_error&) = default;
system_error& operator=(const system_error&) = default;
system_error(system_error&&) = default;
system_error& operator=(system_error&&) = default;
~system_error() FMT_NOEXCEPT FMT_OVERRIDE;
int error_code() const { return error_code_; }
};
......@@ -2698,126 +2810,7 @@ class FMT_API system_error : public std::runtime_error {
\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;
bool fixed;
bool as_percentage;
bool use_locale;
explicit float_spec_handler(char t)
: type(t),
upper(false),
fixed(false),
as_percentage(false),
use_locale(false) {}
void on_general() {
if (type == 'G') upper = true;
}
void on_exp() {
if (type == 'E') upper = true;
}
void on_fixed() {
fixed = true;
if (type == 'F') upper = true;
}
void on_percent() {
fixed = true;
as_percentage = true;
}
void on_hex() {
if (type == 'A') upper = true;
}
void on_num() { use_locale = true; }
FMT_NORETURN void on_error() {
FMT_THROW(format_error("invalid type specifier"));
}
};
template <typename Range>
template <typename T, bool USE_GRISU>
void internal::basic_writer<Range>::write_double(T value,
const format_specs& specs) {
// Check type.
float_spec_handler handler(static_cast<char>(specs.type));
internal::handle_float_type_spec(handler.type, handler);
char sign = 0;
// Use signbit instead of value < 0 since the latter is always false for NaN.
if (std::signbit(value)) {
sign = '-';
value = -value;
} else if (specs.sign != sign::none) {
if (specs.sign == sign::plus)
sign = '+';
else if (specs.sign == sign::space)
sign = ' ';
}
if (!std::isfinite(value)) {
// Format infinity and NaN ourselves because sprintf's output is not
// consistent across platforms.
const char* str = std::isinf(value) ? (handler.upper ? "INF" : "inf")
: (handler.upper ? "NAN" : "nan");
return write_padded(specs,
inf_or_nan_writer{sign, handler.as_percentage, str});
}
if (handler.as_percentage) value *= 100;
memory_buffer buffer;
int exp = 0;
int precision = specs.precision >= 0 || !specs.type ? specs.precision : 6;
bool use_grisu = USE_GRISU &&
(specs.type != 'a' && specs.type != 'A' &&
specs.type != 'e' && specs.type != 'E') &&
internal::grisu_format(static_cast<double>(value), buffer,
precision, handler.fixed ? internal::grisu_options::fixed : 0, exp);
char* decimal_point_pos = nullptr;
if (!use_grisu)
decimal_point_pos = internal::sprintf_format(value, buffer, specs);
if (handler.as_percentage) {
buffer.push_back('%');
--exp; // Adjust decimal place position.
}
format_specs as = specs;
if (specs.align == align::numeric) {
if (sign) {
auto&& it = reserve(1);
*it++ = static_cast<char_type>(sign);
sign = 0;
if (as.width) --as.width;
}
as.align = align::right;
} else if (specs.align == align::none) {
as.align = align::right;
}
char_type decimal_point = handler.use_locale
? internal::decimal_point<char_type>(locale_)
: static_cast<char_type>('.');
if (use_grisu) {
auto params = internal::gen_digits_params();
params.fixed = handler.fixed;
params.num_digits = precision;
params.trailing_zeros =
(precision != 0 && (handler.fixed || !specs.type)) || specs.alt;
write_padded(as, grisu_writer(sign, buffer, exp, params, decimal_point));
} else {
write_padded(as,
double_writer{sign, buffer, decimal_point_pos, decimal_point});
}
}
string_view message) FMT_NOEXCEPT;
// Reports a system error without throwing an exception.
// Can be used to report errors from destructors.
......@@ -2889,7 +2882,7 @@ class format_int {
// Integer division is slow so do it for a group of two digits instead
// of for every digit. The idea comes from the talk by Alexandrescu
// "Three Optimization Tips for C++". See speed-test for a comparison.
unsigned index = static_cast<unsigned>((value % 100) * 2);
auto index = static_cast<unsigned>((value % 100) * 2);
value /= 100;
*--ptr = internal::data::digits[index + 1];
*--ptr = internal::data::digits[index];
......@@ -2898,14 +2891,14 @@ class format_int {
*--ptr = static_cast<char>('0' + value);
return ptr;
}
unsigned index = static_cast<unsigned>(value * 2);
auto index = static_cast<unsigned>(value * 2);
*--ptr = internal::data::digits[index + 1];
*--ptr = internal::data::digits[index];
return ptr;
}
void format_signed(long long value) {
unsigned long long abs_value = static_cast<unsigned long long>(value);
auto abs_value = static_cast<unsigned long long>(value);
bool negative = value < 0;
if (negative) abs_value = 0 - abs_value;
str_ = format_decimal(abs_value);
......@@ -2954,13 +2947,12 @@ template <typename T, typename Char>
struct formatter<T, Char,
enable_if_t<internal::type_constant<T, Char>::value !=
internal::custom_type>> {
FMT_CONSTEXPR formatter() : format_str_(nullptr) {}
FMT_CONSTEXPR formatter() = default;
// Parses format specifiers stopping either at the end of the range or at the
// terminating '}'.
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
format_str_ = ctx.begin();
using handler_type = internal::dynamic_specs_handler<ParseContext>;
auto type = internal::type_constant<T, Char>::value;
internal::specs_checker<handler_type> handler(handler_type(specs_, ctx),
......@@ -2976,6 +2968,8 @@ struct formatter<T, Char,
case internal::uint_type:
case internal::long_long_type:
case internal::ulong_long_type:
case internal::int128_type:
case internal::uint128_type:
case internal::bool_type:
handle_int_type_spec(specs_.type,
internal::int_type_checker<decltype(eh)>(eh));
......@@ -2984,10 +2978,10 @@ struct formatter<T, Char,
handle_char_specs(
&specs_, internal::char_specs_checker<decltype(eh)>(specs_.type, eh));
break;
case internal::float_type:
case internal::double_type:
case internal::long_double_type:
handle_float_type_spec(specs_.type,
internal::float_type_checker<decltype(eh)>(eh));
internal::parse_float_type_spec(specs_, eh);
break;
case internal::cstring_type:
internal::handle_cstring_type_spec(
......@@ -3010,9 +3004,9 @@ struct formatter<T, Char,
template <typename FormatContext>
auto format(const T& val, FormatContext& ctx) -> decltype(ctx.out()) {
internal::handle_dynamic_spec<internal::width_checker>(
specs_.width, specs_.width_ref, ctx, format_str_);
specs_.width, specs_.width_ref, ctx);
internal::handle_dynamic_spec<internal::precision_checker>(
specs_.precision, specs_.precision_ref, ctx, format_str_);
specs_.precision, specs_.precision_ref, ctx);
using range_type =
internal::output_range<typename FormatContext::iterator,
typename FormatContext::char_type>;
......@@ -3022,7 +3016,6 @@ struct formatter<T, Char,
private:
internal::dynamic_format_specs<Char> specs_;
const Char* format_str_;
};
#define FMT_FORMAT_AS(Type, Base) \
......@@ -3040,7 +3033,6 @@ FMT_FORMAT_AS(short, int);
FMT_FORMAT_AS(unsigned short, unsigned);
FMT_FORMAT_AS(long, long long);
FMT_FORMAT_AS(unsigned long, unsigned long long);
FMT_FORMAT_AS(float, double);
FMT_FORMAT_AS(Char*, const Char*);
FMT_FORMAT_AS(std::basic_string<Char>, basic_string_view<Char>);
FMT_FORMAT_AS(std::nullptr_t, const void*);
......@@ -3123,9 +3115,9 @@ template <typename Char = char> class dynamic_formatter {
private:
template <typename Context> void handle_specs(Context& ctx) {
internal::handle_dynamic_spec<internal::width_checker>(
specs_.width, specs_.width_ref, ctx, format_str_);
specs_.width, specs_.width_ref, ctx);
internal::handle_dynamic_spec<internal::precision_checker>(
specs_.precision, specs_.precision_ref, ctx, format_str_);
specs_.precision, specs_.precision_ref, ctx);
}
internal::dynamic_format_specs<Char> specs_;
......@@ -3142,8 +3134,8 @@ basic_format_context<Range, Char>::arg(basic_string_view<char_type> name) {
}
template <typename Char, typename ErrorHandler>
FMT_CONSTEXPR void advance_to(basic_parse_context<Char, ErrorHandler>& ctx,
const Char* p) {
FMT_CONSTEXPR void advance_to(
basic_format_parse_context<Char, ErrorHandler>& ctx, const Char* p) {
ctx.advance_to(ctx.begin() + (p - &*ctx.begin()));
}
......@@ -3175,10 +3167,8 @@ struct format_handler : internal::error_handler {
void on_replacement_field(const Char* p) {
advance_to(parse_context, p);
internal::custom_formatter<Context> f(parse_context, context);
if (!visit_format_arg(f, arg))
context.advance_to(
visit_format_arg(ArgFormatter(context, &parse_context), arg));
context.advance_to(
visit_format_arg(ArgFormatter(context, &parse_context), arg));
}
const Char* on_format_specs(const Char* begin, const Char* end) {
......@@ -3187,7 +3177,7 @@ struct format_handler : internal::error_handler {
if (visit_format_arg(f, arg)) return parse_context.begin();
basic_format_specs<Char> specs;
using internal::specs_handler;
using parse_context_t = basic_parse_context<Char>;
using parse_context_t = basic_format_parse_context<Char>;
internal::specs_checker<specs_handler<parse_context_t, Context>> handler(
specs_handler<parse_context_t, Context>(specs, parse_context, context),
arg.type());
......@@ -3199,7 +3189,7 @@ struct format_handler : internal::error_handler {
return begin;
}
basic_parse_context<Char> parse_context;
basic_format_parse_context<Char> parse_context;
Context context;
basic_format_arg<Context> arg;
};
......@@ -3396,7 +3386,7 @@ template <typename OutputIt> struct format_to_n_result {
template <typename OutputIt, typename Char = typename OutputIt::value_type>
using format_to_n_context =
format_context_t<fmt::internal::truncating_iterator<OutputIt>, Char>;
format_context_t<internal::truncating_iterator<OutputIt>, Char>;
template <typename OutputIt, typename Char = typename OutputIt::value_type>
using format_to_n_args = basic_format_args<format_to_n_context<OutputIt, Char>>;
......@@ -3443,7 +3433,7 @@ inline std::basic_string<Char> internal::vformat(
basic_format_args<buffer_context<Char>> args) {
basic_memory_buffer<Char> buffer;
internal::vformat_to(buffer, format_str, args);
return fmt::to_string(buffer);
return to_string(buffer);
}
/**
......@@ -3452,8 +3442,22 @@ inline std::basic_string<Char> internal::vformat(
*/
template <typename... Args>
inline std::size_t formatted_size(string_view format_str, const Args&... args) {
auto it = format_to(internal::counting_iterator<char>(), format_str, args...);
return it.count();
return format_to(internal::counting_iterator(), format_str, args...).count();
}
template <typename Char, FMT_ENABLE_IF(std::is_same<Char, wchar_t>::value)>
void vprint(std::FILE* f, basic_string_view<Char> format_str,
wformat_args args) {
wmemory_buffer buffer;
internal::vformat_to(buffer, format_str, args);
buffer.push_back(L'\0');
if (std::fputws(buffer.data(), f) == -1)
FMT_THROW(system_error(errno, "cannot write to file"));
}
template <typename Char, FMT_ENABLE_IF(std::is_same<Char, wchar_t>::value)>
void vprint(basic_string_view<Char> format_str, wformat_args args) {
vprint(stdout, format_str, args);
}
#if FMT_USE_USER_DEFINED_LITERALS
......@@ -3466,7 +3470,7 @@ template <typename Char, Char... CHARS> class udl_formatter {
std::basic_string<Char> operator()(Args&&... args) const {
FMT_CONSTEXPR_DECL Char s[] = {CHARS..., '\0'};
FMT_CONSTEXPR_DECL bool invalid_format =
do_check_format_string<Char, error_handler, Args...>(
do_check_format_string<Char, error_handler, remove_cvref_t<Args>...>(
basic_string_view<Char>(s, sizeof...(CHARS)));
(void)invalid_format;
return format(s, std::forward<Args>(args)...);
......@@ -3547,6 +3551,22 @@ FMT_CONSTEXPR internal::udl_arg<wchar_t> operator"" _a(const wchar_t* s,
#endif // FMT_USE_USER_DEFINED_LITERALS
FMT_END_NAMESPACE
#define FMT_STRING_IMPL(s, ...) \
[] { \
struct str : fmt::compile_string { \
using char_type = typename std::remove_cv<std::remove_pointer< \
typename std::decay<decltype(s)>::type>::type>::type; \
__VA_ARGS__ FMT_CONSTEXPR \
operator fmt::basic_string_view<char_type>() const { \
return {s, sizeof(s) / sizeof(char_type) - 1}; \
} \
} result; \
/* Suppress Qt Creator warning about unused operator. */ \
(void)static_cast<fmt::basic_string_view<typename str::char_type>>( \
result); \
return result; \
}()
/**
\rst
Constructs a compile-time format string.
......@@ -3557,37 +3577,10 @@ FMT_END_NAMESPACE
std::string s = format(FMT_STRING("{:d}"), "foo");
\endrst
*/
#define FMT_STRING(s) \
[] { \
struct str : fmt::compile_string { \
using char_type = typename std::remove_cv<std::remove_pointer< \
typename std::decay<decltype(s)>::type>::type>::type; \
FMT_CONSTEXPR operator fmt::basic_string_view<char_type>() const { \
return {s, sizeof(s) / sizeof(char_type) - 1}; \
} \
} result; \
/* Suppress Qt Creator warning about unused operator. */ \
(void)static_cast<fmt::basic_string_view<typename str::char_type>>( \
result); \
return result; \
}()
#define FMT_STRING(s) FMT_STRING_IMPL(s, )
#if defined(FMT_STRING_ALIAS) && FMT_STRING_ALIAS
/**
\rst
Constructs a compile-time format string. This macro is disabled by default to
prevent potential name collisions. To enable it define ``FMT_STRING_ALIAS`` to
1 before including ``fmt/format.h``.
**Example**::
#define FMT_STRING_ALIAS 1
#include <fmt/format.h>
// A compile-time error because 'd' is an invalid specifier for strings.
std::string s = format(fmt("{:d}"), "foo");
\endrst
*/
# define fmt(s) FMT_STRING(s)
# define fmt(s) FMT_STRING_IMPL(s, [[deprecated]])
#endif
#ifdef FMT_HEADER_ONLY
......
......@@ -46,9 +46,13 @@ template <class Char> class formatbuf : public std::basic_streambuf<Char> {
template <typename Char> struct test_stream : std::basic_ostream<Char> {
private:
struct null;
// Hide all operator<< from std::basic_ostream<Char>.
void operator<<(null);
void_t<> operator<<(null<>);
void_t<> operator<<(const Char*);
template <typename T, FMT_ENABLE_IF(std::is_convertible<T, int>::value &&
!std::is_enum<T>::value)>
void_t<> operator<<(T);
};
// Checks if T has a user-defined operator<< (e.g. not a member of
......@@ -56,9 +60,9 @@ template <typename Char> struct test_stream : std::basic_ostream<Char> {
template <typename T, typename Char> class is_streamable {
private:
template <typename U>
static decltype((void)(std::declval<test_stream<Char>&>()
<< std::declval<U>()),
std::true_type())
static bool_constant<!std::is_same<decltype(std::declval<test_stream<Char>&>()
<< std::declval<U>()),
void_t<>>::value>
test(int);
template <typename> static std::false_type test(...);
......@@ -75,8 +79,7 @@ void write(std::basic_ostream<Char>& os, buffer<Char>& buf) {
const Char* buf_data = buf.data();
using unsigned_streamsize = std::make_unsigned<std::streamsize>::type;
unsigned_streamsize size = buf.size();
unsigned_streamsize max_size =
to_unsigned((std::numeric_limits<std::streamsize>::max)());
unsigned_streamsize max_size = to_unsigned(max_value<std::streamsize>());
do {
unsigned_streamsize n = size <= max_size ? size : max_size;
os.write(buf_data, static_cast<std::streamsize>(n));
......@@ -86,9 +89,11 @@ void write(std::basic_ostream<Char>& os, buffer<Char>& buf) {
}
template <typename Char, typename T>
void format_value(buffer<Char>& buf, const T& value) {
void format_value(buffer<Char>& buf, const T& value,
locale_ref loc = locale_ref()) {
formatbuf<Char> format_buf(buf);
std::basic_ostream<Char> output(&format_buf);
if (loc) output.imbue(loc.get<std::locale>());
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
output << value;
buf.resize(buf.size());
......@@ -101,7 +106,7 @@ struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
template <typename Context>
auto format(const T& value, Context& ctx) -> decltype(ctx.out()) {
basic_memory_buffer<Char> buffer;
format_value(buffer, value);
format_value(buffer, value, ctx.locale());
basic_string_view<Char> str(buffer.data(), buffer.size());
return formatter<basic_string_view<Char>, Char>::format(str, ctx);
}
......
......@@ -13,11 +13,10 @@
# undef __STRICT_ANSI__
#endif
#include <errno.h>
#include <fcntl.h> // for O_RDONLY
#include <locale.h> // for locale_t
#include <stdio.h>
#include <stdlib.h> // for strtod_l
#include <cerrno>
#include <clocale> // for locale_t
#include <cstdio>
#include <cstdlib> // for strtod_l
#include <cstddef>
......@@ -27,6 +26,18 @@
#include "format.h"
// UWP doesn't provide _pipe.
#if FMT_HAS_INCLUDE("winapifamily.h")
# include <winapifamily.h>
#endif
#if FMT_HAS_INCLUDE("fcntl.h") && \
(!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
# include <fcntl.h> // for O_RDONLY
# define FMT_USE_FCNTL 1
#else
# define FMT_USE_FCNTL 0
#endif
#ifndef FMT_POSIX
# if defined(_WIN32) && !defined(__MINGW32__)
// Fix warnings about deprecated symbols.
......@@ -54,8 +65,8 @@
#ifndef _WIN32
# define FMT_RETRY_VAL(result, expression, error_result) \
do { \
result = (expression); \
} while (result == error_result && errno == EINTR)
(result) = (expression); \
} while ((result) == (error_result) && errno == EINTR)
#else
# define FMT_RETRY_VAL(result, expression, error_result) result = (expression)
#endif
......@@ -132,16 +143,15 @@ class buffered_file {
explicit buffered_file(FILE* f) : file_(f) {}
public:
buffered_file(const buffered_file&) = delete;
void operator=(const buffered_file&) = delete;
// Constructs a buffered_file object which doesn't represent any file.
buffered_file() FMT_NOEXCEPT : file_(nullptr) {}
// Destroys the object closing the file it represents if any.
FMT_API ~buffered_file() FMT_NOEXCEPT;
private:
buffered_file(const buffered_file&) = delete;
void operator=(const buffered_file&) = delete;
public:
buffered_file(buffered_file&& other) FMT_NOEXCEPT : file_(other.file_) {
other.file_ = nullptr;
......@@ -177,6 +187,7 @@ class buffered_file {
}
};
#if FMT_USE_FCNTL
// A file. Closed file is represented by a file object with descriptor -1.
// Methods that are not declared with FMT_NOEXCEPT may throw
// fmt::system_error in case of failure. Note that some errors such as
......@@ -204,14 +215,13 @@ class file {
// Opens a file and constructs a file object representing this file.
FMT_API file(cstring_view path, int oflag);
private:
public:
file(const file&) = delete;
void operator=(const file&) = delete;
public:
file(file&& other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; }
file& operator=(file&& other) {
file& operator=(file&& other) FMT_NOEXCEPT {
close();
fd_ = other.fd_;
other.fd_ = -1;
......@@ -260,6 +270,7 @@ class file {
// Returns the memory page size.
long getpagesize();
#endif // FMT_USE_FCNTL
#ifdef FMT_LOCALE
// A "C" numeric locale.
......@@ -283,11 +294,10 @@ class Locale {
locale_t locale_;
Locale(const Locale&) = delete;
void operator=(const Locale&) = delete;
public:
using type = locale_t;
Locale(const Locale&) = delete;
void operator=(const Locale&) = delete;
Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", nullptr)) {
if (!locale_) FMT_THROW(system_error(errno, "cannot create locale"));
......
// Formatting library for C++
// Formatting library for C++ - legacy printf implementation
//
// Copyright (c) 2012 - 2016, Victor Zverovich
// All rights reserved.
......@@ -8,7 +8,7 @@
#ifndef FMT_PRINTF_H_
#define FMT_PRINTF_H_
#include <algorithm> // std::fill_n
#include <algorithm> // std::max
#include <limits> // std::numeric_limits
#include "ostream.h"
......@@ -16,15 +16,11 @@
FMT_BEGIN_NAMESPACE
namespace internal {
// A helper function to suppress bogus "conditional expression is constant"
// warnings.
template <typename T> inline T const_check(T value) { return value; }
// Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers.
template <bool IsSigned> struct int_checker {
template <typename T> static bool fits_in_int(T value) {
unsigned max = std::numeric_limits<int>::max();
unsigned max = max_value<int>();
return value <= max;
}
static bool fits_in_int(bool) { return true; }
......@@ -33,7 +29,7 @@ template <bool IsSigned> struct int_checker {
template <> struct int_checker<true> {
template <typename T> static bool fits_in_int(T value) {
return value >= std::numeric_limits<int>::min() &&
value <= std::numeric_limits<int>::max();
value <= max_value<int>();
}
static bool fits_in_int(int) { return true; }
};
......@@ -158,12 +154,12 @@ template <typename Char> class printf_width_handler {
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
unsigned operator()(T value) {
auto width = static_cast<uint32_or_64_t<T>>(value);
auto width = static_cast<uint32_or_64_or_128_t<T>>(value);
if (internal::is_negative(value)) {
specs_.align = align::left;
width = 0 - width;
}
unsigned int_max = std::numeric_limits<int>::max();
unsigned int_max = max_value<int>();
if (width > int_max) FMT_THROW(format_error("number is too big"));
return static_cast<unsigned>(width);
}
......@@ -235,7 +231,7 @@ class printf_arg_formatter : public internal::arg_formatter_base<Range> {
printf_arg_formatter(iterator iter, format_specs& specs, context_type& ctx)
: base(Range(iter), &specs, internal::locale_ref()), context_(ctx) {}
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
template <typename T, FMT_ENABLE_IF(fmt::internal::is_integral<T>::value)>
iterator operator()(T value) {
// MSVC2013 fails to compile separate overloads for bool and char_type so
// use std::is_same instead.
......@@ -332,14 +328,14 @@ template <typename OutputIt, typename Char> class basic_printf_context {
OutputIt out_;
basic_format_args<basic_printf_context> args_;
basic_parse_context<Char> parse_ctx_;
basic_format_parse_context<Char> parse_ctx_;
static void parse_flags(format_specs& specs, const Char*& it,
const Char* end);
// Returns the argument with specified index or, if arg_index is equal
// to the maximum unsigned value, the next argument.
format_arg get_arg(unsigned arg_index = std::numeric_limits<unsigned>::max());
format_arg get_arg(unsigned arg_index = internal::max_value<unsigned>());
// Parses argument index, flags and width and returns the argument index.
unsigned parse_header(const Char*& it, const Char* end, format_specs& specs);
......@@ -361,15 +357,14 @@ template <typename OutputIt, typename Char> class basic_printf_context {
format_arg arg(unsigned id) const { return args_.get(id); }
basic_parse_context<Char>& parse_context() { return parse_ctx_; }
basic_format_parse_context<Char>& parse_context() { return parse_ctx_; }
FMT_CONSTEXPR void on_error(const char* message) {
parse_ctx_.on_error(message);
}
/** Formats stored arguments and writes the output to the range. */
template <typename ArgFormatter =
printf_arg_formatter<internal::buffer_range<Char>>>
template <typename ArgFormatter = printf_arg_formatter<buffer_range<Char>>>
OutputIt format();
};
......@@ -403,7 +398,7 @@ void basic_printf_context<OutputIt, Char>::parse_flags(format_specs& specs,
template <typename OutputIt, typename Char>
typename basic_printf_context<OutputIt, Char>::format_arg
basic_printf_context<OutputIt, Char>::get_arg(unsigned arg_index) {
if (arg_index == std::numeric_limits<unsigned>::max())
if (arg_index == internal::max_value<unsigned>())
arg_index = parse_ctx_.next_arg_id();
else
parse_ctx_.check_arg_id(--arg_index);
......@@ -413,7 +408,7 @@ basic_printf_context<OutputIt, Char>::get_arg(unsigned arg_index) {
template <typename OutputIt, typename Char>
unsigned basic_printf_context<OutputIt, Char>::parse_header(
const Char*& it, const Char* end, format_specs& specs) {
unsigned arg_index = std::numeric_limits<unsigned>::max();
unsigned arg_index = internal::max_value<unsigned>();
char_type c = *it;
if (c >= '0' && c <= '9') {
// Parse an argument index (if followed by '$') or a width possibly
......@@ -470,6 +465,7 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
// Parse argument index, flags and width.
unsigned arg_index = parse_header(it, end, specs);
if (arg_index == 0) on_error("argument index out of range");
// Parse precision.
if (it != end && *it == '.') {
......
......@@ -246,7 +246,8 @@ template <typename T, typename Char> struct is_range {
static FMT_CONSTEXPR_DECL const bool value =
internal::is_range_<T>::value &&
!internal::is_like_std_string<T>::value &&
!std::is_convertible<T, std::basic_string<Char>>::value;
!std::is_convertible<T, std::basic_string<Char>>::value &&
!std::is_constructible<internal::std_string_view<Char>, T>::value;
};
template <typename RangeT, typename Char>
......@@ -283,6 +284,82 @@ struct formatter<RangeT, Char,
}
};
template <typename Char, typename... T> struct tuple_arg_join : internal::view {
const std::tuple<T...>& tuple;
basic_string_view<Char> sep;
tuple_arg_join(const std::tuple<T...>& t, basic_string_view<Char> s)
: tuple{t}, sep{s} {}
};
template <typename Char, typename... T>
struct formatter<tuple_arg_join<Char, T...>, Char> {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
typename FormatContext::iterator format(
const tuple_arg_join<Char, T...>& value, FormatContext& ctx) {
return format(value, ctx, internal::make_index_sequence<sizeof...(T)>{});
}
private:
template <typename FormatContext, size_t... N>
typename FormatContext::iterator format(
const tuple_arg_join<Char, T...>& value, FormatContext& ctx,
internal::index_sequence<N...>) {
return format_args(value, ctx, std::get<N>(value.tuple)...);
}
template <typename FormatContext>
typename FormatContext::iterator format_args(
const tuple_arg_join<Char, T...>&, FormatContext& ctx) {
// NOTE: for compilers that support C++17, this empty function instantiation
// can be replaced with a constexpr branch in the variadic overload.
return ctx.out();
}
template <typename FormatContext, typename Arg, typename... Args>
typename FormatContext::iterator format_args(
const tuple_arg_join<Char, T...>& value, FormatContext& ctx,
const Arg& arg, const Args&... args) {
using base = formatter<typename std::decay<Arg>::type, Char>;
auto out = ctx.out();
out = base{}.format(arg, ctx);
if (sizeof...(Args) > 0) {
out = std::copy(value.sep.begin(), value.sep.end(), out);
ctx.advance_to(out);
return format_args(value, ctx, args...);
}
return out;
}
};
/**
\rst
Returns an object that formats `tuple` with elements separated by `sep`.
**Example**::
std::tuple<int, char> t = {1, 'a'};
fmt::print("{}", fmt::join(t, ", "));
// Output: "1, a"
\endrst
*/
template <typename... T>
FMT_CONSTEXPR tuple_arg_join<char, T...> join(const std::tuple<T...>& tuple,
string_view sep) {
return {tuple, sep};
}
template <typename... T>
FMT_CONSTEXPR tuple_arg_join<wchar_t, T...> join(const std::tuple<T...>& tuple,
wstring_view sep) {
return {tuple, sep};
}
FMT_END_NAMESPACE
#endif // FMT_RANGES_H_
/*
* For conversion between std::chrono::durations without undefined
* behaviour or erroneous results.
* This is a stripped down version of duration_cast, for inclusion in fmt.
* See https://github.com/pauldreik/safe_duration_cast
*
* Copyright Paul Dreik 2019
*
* This file is licensed under the fmt license, see format.h
*/
#include <chrono>
#include <cmath>
#include <limits>
#include <type_traits>
#include "format.h"
FMT_BEGIN_NAMESPACE
namespace safe_duration_cast {
template <typename To, typename From,
FMT_ENABLE_IF(!std::is_same<From, To>::value &&
std::numeric_limits<From>::is_signed ==
std::numeric_limits<To>::is_signed)>
FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
ec = 0;
using F = std::numeric_limits<From>;
using T = std::numeric_limits<To>;
static_assert(F::is_integer, "From must be integral");
static_assert(T::is_integer, "To must be integral");
// A and B are both signed, or both unsigned.
if (F::digits <= T::digits) {
// From fits in To without any problem.
} else {
// From does not always fit in To, resort to a dynamic check.
if (from < T::min() || from > T::max()) {
// outside range.
ec = 1;
return {};
}
}
return static_cast<To>(from);
}
/**
* converts From to To, without loss. If the dynamic value of from
* can't be converted to To without loss, ec is set.
*/
template <typename To, typename From,
FMT_ENABLE_IF(!std::is_same<From, To>::value &&
std::numeric_limits<From>::is_signed !=
std::numeric_limits<To>::is_signed)>
FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
ec = 0;
using F = std::numeric_limits<From>;
using T = std::numeric_limits<To>;
static_assert(F::is_integer, "From must be integral");
static_assert(T::is_integer, "To must be integral");
if (F::is_signed && !T::is_signed) {
// From may be negative, not allowed!
if (from < 0) {
ec = 1;
return {};
}
// From is positive. Can it always fit in To?
if (F::digits <= T::digits) {
// yes, From always fits in To.
} else {
// from may not fit in To, we have to do a dynamic check
if (from > static_cast<From>(T::max())) {
ec = 1;
return {};
}
}
}
if (!F::is_signed && T::is_signed) {
// can from be held in To?
if (F::digits < T::digits) {
// yes, From always fits in To.
} else {
// from may not fit in To, we have to do a dynamic check
if (from > static_cast<From>(T::max())) {
// outside range.
ec = 1;
return {};
}
}
}
// reaching here means all is ok for lossless conversion.
return static_cast<To>(from);
} // function
template <typename To, typename From,
FMT_ENABLE_IF(std::is_same<From, To>::value)>
FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
ec = 0;
return from;
} // function
// clang-format off
/**
* converts From to To if possible, otherwise ec is set.
*
* input | output
* ---------------------------------|---------------
* NaN | NaN
* Inf | Inf
* normal, fits in output | converted (possibly lossy)
* normal, does not fit in output | ec is set
* subnormal | best effort
* -Inf | -Inf
*/
// clang-format on
template <typename To, typename From,
FMT_ENABLE_IF(!std::is_same<From, To>::value)>
FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
ec = 0;
using T = std::numeric_limits<To>;
static_assert(std::is_floating_point<From>::value, "From must be floating");
static_assert(std::is_floating_point<To>::value, "To must be floating");
// catch the only happy case
if (std::isfinite(from)) {
if (from >= T::lowest() && from <= T::max()) {
return static_cast<To>(from);
}
// not within range.
ec = 1;
return {};
}
// nan and inf will be preserved
return static_cast<To>(from);
} // function
template <typename To, typename From,
FMT_ENABLE_IF(std::is_same<From, To>::value)>
FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
ec = 0;
static_assert(std::is_floating_point<From>::value, "From must be floating");
return from;
}
/**
* safe duration cast between integral durations
*/
template <typename To, typename FromRep, typename FromPeriod,
FMT_ENABLE_IF(std::is_integral<FromRep>::value),
FMT_ENABLE_IF(std::is_integral<typename To::rep>::value)>
To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
int& ec) {
using From = std::chrono::duration<FromRep, FromPeriod>;
ec = 0;
// the basic idea is that we need to convert from count() in the from type
// to count() in the To type, by multiplying it with this:
using Factor = std::ratio_divide<typename From::period, typename To::period>;
static_assert(Factor::num > 0, "num must be positive");
static_assert(Factor::den > 0, "den must be positive");
// the conversion is like this: multiply from.count() with Factor::num
// /Factor::den and convert it to To::rep, all this without
// overflow/underflow. let's start by finding a suitable type that can hold
// both To, From and Factor::num
using IntermediateRep =
typename std::common_type<typename From::rep, typename To::rep,
decltype(Factor::num)>::type;
// safe conversion to IntermediateRep
IntermediateRep count =
lossless_integral_conversion<IntermediateRep>(from.count(), ec);
if (ec) {
return {};
}
// multiply with Factor::num without overflow or underflow
if (Factor::num != 1) {
constexpr auto max1 =
std::numeric_limits<IntermediateRep>::max() / Factor::num;
if (count > max1) {
ec = 1;
return {};
}
constexpr auto min1 =
std::numeric_limits<IntermediateRep>::min() / Factor::num;
if (count < min1) {
ec = 1;
return {};
}
count *= Factor::num;
}
// this can't go wrong, right? den>0 is checked earlier.
if (Factor::den != 1) {
count /= Factor::den;
}
// convert to the to type, safely
using ToRep = typename To::rep;
const ToRep tocount = lossless_integral_conversion<ToRep>(count, ec);
if (ec) {
return {};
}
return To{tocount};
}
/**
* safe duration_cast between floating point durations
*/
template <typename To, typename FromRep, typename FromPeriod,
FMT_ENABLE_IF(std::is_floating_point<FromRep>::value),
FMT_ENABLE_IF(std::is_floating_point<typename To::rep>::value)>
To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
int& ec) {
using From = std::chrono::duration<FromRep, FromPeriod>;
ec = 0;
if (std::isnan(from.count())) {
// nan in, gives nan out. easy.
return To{std::numeric_limits<typename To::rep>::quiet_NaN()};
}
// maybe we should also check if from is denormal, and decide what to do about
// it.
// +-inf should be preserved.
if (std::isinf(from.count())) {
return To{from.count()};
}
// the basic idea is that we need to convert from count() in the from type
// to count() in the To type, by multiplying it with this:
using Factor = std::ratio_divide<typename From::period, typename To::period>;
static_assert(Factor::num > 0, "num must be positive");
static_assert(Factor::den > 0, "den must be positive");
// the conversion is like this: multiply from.count() with Factor::num
// /Factor::den and convert it to To::rep, all this without
// overflow/underflow. let's start by finding a suitable type that can hold
// both To, From and Factor::num
using IntermediateRep =
typename std::common_type<typename From::rep, typename To::rep,
decltype(Factor::num)>::type;
// force conversion of From::rep -> IntermediateRep to be safe,
// even if it will never happen be narrowing in this context.
IntermediateRep count =
safe_float_conversion<IntermediateRep>(from.count(), ec);
if (ec) {
return {};
}
// multiply with Factor::num without overflow or underflow
if (Factor::num != 1) {
constexpr auto max1 = std::numeric_limits<IntermediateRep>::max() /
static_cast<IntermediateRep>(Factor::num);
if (count > max1) {
ec = 1;
return {};
}
constexpr auto min1 = std::numeric_limits<IntermediateRep>::lowest() /
static_cast<IntermediateRep>(Factor::num);
if (count < min1) {
ec = 1;
return {};
}
count *= static_cast<IntermediateRep>(Factor::num);
}
// this can't go wrong, right? den>0 is checked earlier.
if (Factor::den != 1) {
using common_t = typename std::common_type<IntermediateRep, intmax_t>::type;
count /= static_cast<common_t>(Factor::den);
}
// convert to the to type, safely
using ToRep = typename To::rep;
const ToRep tocount = safe_float_conversion<ToRep>(count, ec);
if (ec) {
return {};
}
return To{tocount};
}
} // namespace safe_duration_cast
FMT_END_NAMESPACE
......@@ -9,27 +9,60 @@
#if !defined(SPDLOG_FMT_EXTERNAL)
#include "spdlog/fmt/bundled/format-inl.h"
FMT_BEGIN_NAMESPACE
template struct internal::basic_data<void>;
template FMT_API internal::locale_ref::locale_ref(const std::locale &loc);
template struct FMT_API internal::basic_data<void>;
// Workaround a bug in MSVC2013 that prevents instantiation of format_float.
int (*instantiate_format_float)(double, int, internal::float_specs,
internal::buffer<char>&) =
internal::format_float;
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
template FMT_API internal::locale_ref::locale_ref(const std::locale& loc);
template FMT_API std::locale internal::locale_ref::get<std::locale>() const;
#endif
// Explicit instantiations for char.
template FMT_API std::string internal::grouping_impl<char>(locale_ref);
template FMT_API char internal::thousands_sep_impl(locale_ref);
template FMT_API char internal::decimal_point_impl(locale_ref);
template FMT_API void internal::buffer<char>::append(const char *, const char *);
template FMT_API void internal::arg_map<format_context>::init(const basic_format_args<format_context> &args);
template FMT_API std::string internal::vformat<char>(string_view, basic_format_args<format_context>);
template FMT_API format_context::iterator internal::vformat_to(internal::buffer<char> &, string_view, basic_format_args<format_context>);
template FMT_API char *internal::sprintf_format(double, internal::buffer<char> &, sprintf_specs);
template FMT_API char *internal::sprintf_format(long double, internal::buffer<char> &, sprintf_specs);
template FMT_API void internal::buffer<char>::append(const char*, const char*);
template FMT_API void internal::arg_map<format_context>::init(
const basic_format_args<format_context>& args);
template FMT_API std::string internal::vformat<char>(
string_view, basic_format_args<format_context>);
template FMT_API format_context::iterator internal::vformat_to(
internal::buffer<char>&, string_view, basic_format_args<format_context>);
template FMT_API int internal::snprintf_float(double, int,
internal::float_specs,
internal::buffer<char>&);
template FMT_API int internal::snprintf_float(long double, int,
internal::float_specs,
internal::buffer<char>&);
template FMT_API int internal::format_float(double, int, internal::float_specs,
internal::buffer<char>&);
template FMT_API int internal::format_float(long double, int,
internal::float_specs,
internal::buffer<char>&);
// Explicit instantiations for wchar_t.
template FMT_API std::string internal::grouping_impl<wchar_t>(locale_ref);
template FMT_API wchar_t internal::thousands_sep_impl(locale_ref);
template FMT_API wchar_t internal::decimal_point_impl(locale_ref);
template FMT_API void internal::buffer<wchar_t>::append(const wchar_t *, const wchar_t *);
template FMT_API void internal::arg_map<wformat_context>::init(const basic_format_args<wformat_context> &);
template FMT_API std::wstring internal::vformat<wchar_t>(wstring_view, basic_format_args<wformat_context>);
template FMT_API void internal::buffer<wchar_t>::append(const wchar_t*,
const wchar_t*);
template FMT_API std::wstring internal::vformat<wchar_t>(
wstring_view, basic_format_args<wformat_context>);
FMT_END_NAMESPACE
#endif
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