Merge branch 'master' into moiwi-opt
This commit is contained in:
commit
d08fd870e7
@ -25,6 +25,85 @@
|
|||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
|
template <typename OutputIt> class truncating_iterator_base {
|
||||||
|
protected:
|
||||||
|
OutputIt out_;
|
||||||
|
size_t limit_;
|
||||||
|
size_t count_ = 0;
|
||||||
|
|
||||||
|
truncating_iterator_base() : out_(), limit_(0) {}
|
||||||
|
|
||||||
|
truncating_iterator_base(OutputIt out, size_t limit)
|
||||||
|
: out_(out), limit_(limit) {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
using iterator_category = std::output_iterator_tag;
|
||||||
|
using value_type = typename std::iterator_traits<OutputIt>::value_type;
|
||||||
|
using difference_type = std::ptrdiff_t;
|
||||||
|
using pointer = void;
|
||||||
|
using reference = void;
|
||||||
|
using _Unchecked_type =
|
||||||
|
truncating_iterator_base; // Mark iterator as checked.
|
||||||
|
|
||||||
|
OutputIt base() const { return out_; }
|
||||||
|
size_t count() const { return count_; }
|
||||||
|
};
|
||||||
|
|
||||||
|
// An output iterator that truncates the output and counts the number of objects
|
||||||
|
// written to it.
|
||||||
|
template <typename OutputIt,
|
||||||
|
typename Enable = typename std::is_void<
|
||||||
|
typename std::iterator_traits<OutputIt>::value_type>::type>
|
||||||
|
class truncating_iterator;
|
||||||
|
|
||||||
|
template <typename OutputIt>
|
||||||
|
class truncating_iterator<OutputIt, std::false_type>
|
||||||
|
: public truncating_iterator_base<OutputIt> {
|
||||||
|
mutable typename truncating_iterator_base<OutputIt>::value_type blackhole_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using value_type = typename truncating_iterator_base<OutputIt>::value_type;
|
||||||
|
|
||||||
|
truncating_iterator() = default;
|
||||||
|
|
||||||
|
truncating_iterator(OutputIt out, size_t limit)
|
||||||
|
: truncating_iterator_base<OutputIt>(out, limit) {}
|
||||||
|
|
||||||
|
truncating_iterator& operator++() {
|
||||||
|
if (this->count_++ < this->limit_) ++this->out_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
truncating_iterator operator++(int) {
|
||||||
|
auto it = *this;
|
||||||
|
++*this;
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
value_type& operator*() const {
|
||||||
|
return this->count_ < this->limit_ ? *this->out_ : blackhole_;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename OutputIt>
|
||||||
|
class truncating_iterator<OutputIt, std::true_type>
|
||||||
|
: public truncating_iterator_base<OutputIt> {
|
||||||
|
public:
|
||||||
|
truncating_iterator() = default;
|
||||||
|
|
||||||
|
truncating_iterator(OutputIt out, size_t limit)
|
||||||
|
: truncating_iterator_base<OutputIt>(out, limit) {}
|
||||||
|
|
||||||
|
template <typename T> truncating_iterator& operator=(T val) {
|
||||||
|
if (this->count_++ < this->limit_) *this->out_++ = val;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
truncating_iterator& operator++() { return *this; }
|
||||||
|
truncating_iterator& operator++(int) { return *this; }
|
||||||
|
truncating_iterator& operator*() { return *this; }
|
||||||
|
};
|
||||||
|
|
||||||
// A compile-time string which is compiled into fast formatting code.
|
// A compile-time string which is compiled into fast formatting code.
|
||||||
class compiled_string {};
|
class compiled_string {};
|
||||||
|
|
||||||
@ -223,9 +302,13 @@ template <typename OutputIt, typename Context, typename Id>
|
|||||||
void format_arg(
|
void format_arg(
|
||||||
basic_format_parse_context<typename Context::char_type>& parse_ctx,
|
basic_format_parse_context<typename Context::char_type>& parse_ctx,
|
||||||
Context& ctx, Id arg_id) {
|
Context& ctx, Id arg_id) {
|
||||||
ctx.advance_to(visit_format_arg(
|
auto arg = ctx.arg(arg_id);
|
||||||
arg_formatter<OutputIt, typename Context::char_type>(ctx, &parse_ctx),
|
if (arg.type() == type::custom_type) {
|
||||||
ctx.arg(arg_id)));
|
visit_format_arg(custom_formatter<Context>(parse_ctx, ctx), arg);
|
||||||
|
} else {
|
||||||
|
ctx.advance_to(visit_format_arg(
|
||||||
|
arg_formatter<OutputIt, typename Context::char_type>(ctx), arg));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// vformat_to is defined in a subnamespace to prevent ADL.
|
// vformat_to is defined in a subnamespace to prevent ADL.
|
||||||
@ -287,7 +370,7 @@ auto vformat_to(OutputIt out, CompiledFormat& cf,
|
|||||||
advance_to(parse_ctx, part.arg_id_end);
|
advance_to(parse_ctx, part.arg_id_end);
|
||||||
ctx.advance_to(
|
ctx.advance_to(
|
||||||
visit_format_arg(arg_formatter<OutputIt, typename Context::char_type>(
|
visit_format_arg(arg_formatter<OutputIt, typename Context::char_type>(
|
||||||
ctx, nullptr, &specs),
|
ctx, &specs),
|
||||||
arg));
|
arg));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -849,7 +932,10 @@ format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n, const S&,
|
|||||||
return {it.base(), it.count()};
|
return {it.base(), it.count()};
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename CompiledFormat, typename... Args>
|
template <typename CompiledFormat, typename... Args,
|
||||||
|
FMT_ENABLE_IF(std::is_base_of<detail::basic_compiled_format,
|
||||||
|
CompiledFormat>::value ||
|
||||||
|
detail::is_compiled_string<CompiledFormat>::value)>
|
||||||
size_t formatted_size(const CompiledFormat& cf, const Args&... args) {
|
size_t formatted_size(const CompiledFormat& cf, const Args&... args) {
|
||||||
return format_to(detail::counting_iterator(), cf, args...).count();
|
return format_to(detail::counting_iterator(), cf, args...).count();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1865,11 +1865,11 @@ inline auto format_to_n(OutputIt out, size_t n, const S& format_str,
|
|||||||
Returns the number of characters in the output of
|
Returns the number of characters in the output of
|
||||||
``format(format_str, args...)``.
|
``format(format_str, args...)``.
|
||||||
*/
|
*/
|
||||||
template <typename... Args>
|
template <typename S, typename... Args, typename Char = char_t<S>>
|
||||||
inline size_t formatted_size(string_view format_str, Args&&... args) {
|
inline size_t formatted_size(const S& format_str, Args&&... args) {
|
||||||
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
|
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
|
||||||
detail::counting_buffer<> buf;
|
detail::counting_buffer<> buf;
|
||||||
detail::vformat_to(buf, format_str, vargs);
|
detail::vformat_to(buf, to_string_view(format_str), vargs);
|
||||||
return buf.count();
|
return buf.count();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1066,6 +1066,7 @@ template <typename T> const char basic_data<T>::signs[] = {0, '-', '+', ' '};
|
|||||||
|
|
||||||
#if __cplusplus < 201703L
|
#if __cplusplus < 201703L
|
||||||
template <typename T> constexpr const char basic_data<T>::hex_digits[];
|
template <typename T> constexpr const char basic_data<T>::hex_digits[];
|
||||||
|
template <typename T> constexpr const unsigned basic_data<T>::prefixes[];
|
||||||
template <typename T> constexpr const char basic_data<T>::left_padding_shifts[];
|
template <typename T> constexpr const char basic_data<T>::left_padding_shifts[];
|
||||||
template <typename T>
|
template <typename T>
|
||||||
constexpr const char basic_data<T>::right_padding_shifts[];
|
constexpr const char basic_data<T>::right_padding_shifts[];
|
||||||
|
|||||||
@ -121,8 +121,8 @@ FMT_END_NAMESPACE
|
|||||||
# define FMT_THROW(x) throw x
|
# define FMT_THROW(x) throw x
|
||||||
# endif
|
# endif
|
||||||
# else
|
# else
|
||||||
# define FMT_THROW(x) \
|
# define FMT_THROW(x) \
|
||||||
do { \
|
do { \
|
||||||
FMT_ASSERT(false, (x).what()); \
|
FMT_ASSERT(false, (x).what()); \
|
||||||
} while (false)
|
} while (false)
|
||||||
# endif
|
# endif
|
||||||
@ -174,9 +174,9 @@ FMT_END_NAMESPACE
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Defining FMT_REDUCE_INT_INSTANTIATIONS to 1, will reduce the number of
|
// Defining FMT_REDUCE_INT_INSTANTIATIONS to 1, will reduce the number of
|
||||||
// int_writer template instances to just one by only using the largest integer
|
// integer formatter template instantiations to just one by only using the
|
||||||
// type. This results in a reduction in binary size but will cause a decrease in
|
// largest integer type. This results in a reduction in binary size but will
|
||||||
// integer formatting performance.
|
// cause a decrease in integer formatting performance.
|
||||||
#if !defined(FMT_REDUCE_INT_INSTANTIATIONS)
|
#if !defined(FMT_REDUCE_INT_INSTANTIATIONS)
|
||||||
# define FMT_REDUCE_INT_INSTANTIATIONS 0
|
# define FMT_REDUCE_INT_INSTANTIATIONS 0
|
||||||
#endif
|
#endif
|
||||||
@ -402,6 +402,10 @@ template <typename Iterator> constexpr Iterator& reserve(Iterator& it, size_t) {
|
|||||||
return it;
|
return it;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt>
|
||||||
|
using reserve_iterator =
|
||||||
|
remove_reference_t<decltype(reserve(std::declval<OutputIt&>(), 0))>;
|
||||||
|
|
||||||
template <typename T, typename OutputIt>
|
template <typename T, typename OutputIt>
|
||||||
constexpr T* to_pointer(OutputIt, size_t) {
|
constexpr T* to_pointer(OutputIt, size_t) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@ -465,85 +469,6 @@ class counting_iterator {
|
|||||||
value_type operator*() const { return {}; }
|
value_type operator*() const { return {}; }
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename OutputIt> class truncating_iterator_base {
|
|
||||||
protected:
|
|
||||||
OutputIt out_;
|
|
||||||
size_t limit_;
|
|
||||||
size_t count_ = 0;
|
|
||||||
|
|
||||||
truncating_iterator_base() : out_(), limit_(0) {}
|
|
||||||
|
|
||||||
truncating_iterator_base(OutputIt out, size_t limit)
|
|
||||||
: out_(out), limit_(limit) {}
|
|
||||||
|
|
||||||
public:
|
|
||||||
using iterator_category = std::output_iterator_tag;
|
|
||||||
using value_type = typename std::iterator_traits<OutputIt>::value_type;
|
|
||||||
using difference_type = std::ptrdiff_t;
|
|
||||||
using pointer = void;
|
|
||||||
using reference = void;
|
|
||||||
using _Unchecked_type =
|
|
||||||
truncating_iterator_base; // Mark iterator as checked.
|
|
||||||
|
|
||||||
OutputIt base() const { return out_; }
|
|
||||||
size_t count() const { return count_; }
|
|
||||||
};
|
|
||||||
|
|
||||||
// An output iterator that truncates the output and counts the number of objects
|
|
||||||
// written to it.
|
|
||||||
template <typename OutputIt,
|
|
||||||
typename Enable = typename std::is_void<
|
|
||||||
typename std::iterator_traits<OutputIt>::value_type>::type>
|
|
||||||
class truncating_iterator;
|
|
||||||
|
|
||||||
template <typename OutputIt>
|
|
||||||
class truncating_iterator<OutputIt, std::false_type>
|
|
||||||
: public truncating_iterator_base<OutputIt> {
|
|
||||||
mutable typename truncating_iterator_base<OutputIt>::value_type blackhole_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
using value_type = typename truncating_iterator_base<OutputIt>::value_type;
|
|
||||||
|
|
||||||
truncating_iterator() = default;
|
|
||||||
|
|
||||||
truncating_iterator(OutputIt out, size_t limit)
|
|
||||||
: truncating_iterator_base<OutputIt>(out, limit) {}
|
|
||||||
|
|
||||||
truncating_iterator& operator++() {
|
|
||||||
if (this->count_++ < this->limit_) ++this->out_;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
truncating_iterator operator++(int) {
|
|
||||||
auto it = *this;
|
|
||||||
++*this;
|
|
||||||
return it;
|
|
||||||
}
|
|
||||||
|
|
||||||
value_type& operator*() const {
|
|
||||||
return this->count_ < this->limit_ ? *this->out_ : blackhole_;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename OutputIt>
|
|
||||||
class truncating_iterator<OutputIt, std::true_type>
|
|
||||||
: public truncating_iterator_base<OutputIt> {
|
|
||||||
public:
|
|
||||||
truncating_iterator() = default;
|
|
||||||
|
|
||||||
truncating_iterator(OutputIt out, size_t limit)
|
|
||||||
: truncating_iterator_base<OutputIt>(out, limit) {}
|
|
||||||
|
|
||||||
template <typename T> truncating_iterator& operator=(T val) {
|
|
||||||
if (this->count_++ < this->limit_) *this->out_++ = val;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
truncating_iterator& operator++() { return *this; }
|
|
||||||
truncating_iterator& operator++(int) { return *this; }
|
|
||||||
truncating_iterator& operator*() { return *this; }
|
|
||||||
};
|
|
||||||
|
|
||||||
// <algorithm> is spectacularly slow to compile in C++20 so use a simple fill_n
|
// <algorithm> is spectacularly slow to compile in C++20 so use a simple fill_n
|
||||||
// instead (#1998).
|
// instead (#1998).
|
||||||
template <typename OutputIt, typename Size, typename T>
|
template <typename OutputIt, typename Size, typename T>
|
||||||
@ -977,6 +902,8 @@ using uint32_or_64_or_128_t =
|
|||||||
conditional_t<num_bits<T>() <= 32 && !FMT_REDUCE_INT_INSTANTIATIONS,
|
conditional_t<num_bits<T>() <= 32 && !FMT_REDUCE_INT_INSTANTIATIONS,
|
||||||
uint32_t,
|
uint32_t,
|
||||||
conditional_t<num_bits<T>() <= 64, uint64_t, uint128_t>>;
|
conditional_t<num_bits<T>() <= 64, uint64_t, uint128_t>>;
|
||||||
|
template <typename T>
|
||||||
|
using uint64_or_128_t = conditional_t<num_bits<T>() <= 64, uint64_t, uint128_t>;
|
||||||
|
|
||||||
// 128-bit integer type used internally
|
// 128-bit integer type used internally
|
||||||
struct FMT_EXTERN_TEMPLATE_API uint128_wrapper {
|
struct FMT_EXTERN_TEMPLATE_API uint128_wrapper {
|
||||||
@ -1055,8 +982,10 @@ template <typename T = void> struct FMT_EXTERN_TEMPLATE_API basic_data {
|
|||||||
static const char reset_color[5];
|
static const char reset_color[5];
|
||||||
static const wchar_t wreset_color[5];
|
static const wchar_t wreset_color[5];
|
||||||
static const char signs[];
|
static const char signs[];
|
||||||
static constexpr const char left_padding_shifts[5] = {31, 31, 0, 1, 0};
|
static constexpr const unsigned prefixes[] = {0, 0, 0x1000000u | '+',
|
||||||
static constexpr const char right_padding_shifts[5] = {0, 31, 0, 1, 0};
|
0x1000000u | ' '};
|
||||||
|
static constexpr const char left_padding_shifts[] = {31, 31, 0, 1, 0};
|
||||||
|
static constexpr const char right_padding_shifts[] = {0, 31, 0, 1, 0};
|
||||||
|
|
||||||
// DEPRECATED! These are for ABI compatibility.
|
// DEPRECATED! These are for ABI compatibility.
|
||||||
static const uint32_t zero_or_powers_of_10_32[];
|
static const uint32_t zero_or_powers_of_10_32[];
|
||||||
@ -1095,44 +1024,33 @@ template <typename T> FMT_CONSTEXPR int count_digits_fallback(T n) {
|
|||||||
count += 4;
|
count += 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if FMT_USE_INT128
|
||||||
|
FMT_CONSTEXPR inline int count_digits(uint128_t n) {
|
||||||
|
return count_digits_fallback(n);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef FMT_BUILTIN_CLZLL
|
|
||||||
// Returns the number of decimal digits in n. Leading zeros are not counted
|
// Returns the number of decimal digits in n. Leading zeros are not counted
|
||||||
// except for n == 0 in which case count_digits returns 1.
|
// except for n == 0 in which case count_digits returns 1.
|
||||||
FMT_CONSTEXPR20 inline int count_digits(uint64_t n) {
|
FMT_CONSTEXPR20 inline int count_digits(uint64_t n) {
|
||||||
if (is_constant_evaluated()) {
|
if (is_constant_evaluated()) {
|
||||||
return count_digits_fallback(n);
|
return count_digits_fallback(n);
|
||||||
}
|
}
|
||||||
|
#ifdef FMT_BUILTIN_CLZLL
|
||||||
// https://github.com/fmtlib/format-benchmark/blob/master/digits10
|
// https://github.com/fmtlib/format-benchmark/blob/master/digits10
|
||||||
auto t = bsr2log10(FMT_BUILTIN_CLZLL(n | 1) ^ 63);
|
auto t = bsr2log10(FMT_BUILTIN_CLZLL(n | 1) ^ 63);
|
||||||
return t - (n < data::zero_or_powers_of_10_64_new[t]);
|
return t - (n < data::zero_or_powers_of_10_64_new[t]);
|
||||||
}
|
|
||||||
#else
|
#else
|
||||||
// Fallback version of count_digits used when __builtin_clz is not available.
|
|
||||||
FMT_CONSTEXPR inline int count_digits(uint64_t n) {
|
|
||||||
return count_digits_fallback(n);
|
return count_digits_fallback(n);
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if FMT_USE_INT128
|
|
||||||
FMT_CONSTEXPR 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).
|
// Counts the number of digits in n. BITS = log2(radix).
|
||||||
template <unsigned BITS, typename UInt> FMT_CONSTEXPR int count_digits(UInt n) {
|
template <int BITS, typename UInt> FMT_CONSTEXPR int count_digits(UInt n) {
|
||||||
|
#ifdef FMT_BUILTIN_CLZ
|
||||||
|
if (num_bits<UInt>() == 32)
|
||||||
|
return (FMT_BUILTIN_CLZ(static_cast<uint32_t>(n) | 1) ^ 31) / BITS + 1;
|
||||||
|
#endif
|
||||||
int num_digits = 0;
|
int num_digits = 0;
|
||||||
do {
|
do {
|
||||||
++num_digits;
|
++num_digits;
|
||||||
@ -1702,14 +1620,14 @@ FMT_CONSTEXPR OutputIt write_padded(OutputIt out,
|
|||||||
static_assert(align == align::left || align == align::right, "");
|
static_assert(align == align::left || align == align::right, "");
|
||||||
unsigned spec_width = to_unsigned(specs.width);
|
unsigned spec_width = to_unsigned(specs.width);
|
||||||
size_t padding = spec_width > width ? spec_width - width : 0;
|
size_t padding = spec_width > width ? spec_width - width : 0;
|
||||||
size_t left_padding = 0;
|
|
||||||
auto* shifts = align == align::left ? data::left_padding_shifts
|
auto* shifts = align == align::left ? data::left_padding_shifts
|
||||||
: data::right_padding_shifts;
|
: data::right_padding_shifts;
|
||||||
left_padding = padding >> shifts[specs.align];
|
size_t left_padding = padding >> shifts[specs.align];
|
||||||
|
size_t right_padding = padding - left_padding;
|
||||||
auto it = reserve(out, size + padding * specs.fill.size());
|
auto it = reserve(out, size + padding * specs.fill.size());
|
||||||
it = fill(it, left_padding, specs.fill);
|
if (left_padding != 0) it = fill(it, left_padding, specs.fill);
|
||||||
it = f(it);
|
it = f(it);
|
||||||
it = fill(it, padding - left_padding, specs.fill);
|
if (right_padding != 0) it = fill(it, padding - left_padding, specs.fill);
|
||||||
return base_iterator(out, it);
|
return base_iterator(out, it);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1724,18 +1642,17 @@ constexpr OutputIt write_padded(OutputIt out,
|
|||||||
template <typename Char, typename OutputIt>
|
template <typename Char, typename OutputIt>
|
||||||
OutputIt write_bytes(OutputIt out, string_view bytes,
|
OutputIt write_bytes(OutputIt out, string_view bytes,
|
||||||
const basic_format_specs<Char>& specs) {
|
const basic_format_specs<Char>& specs) {
|
||||||
using iterator = remove_reference_t<decltype(reserve(out, 0))>;
|
return write_padded(out, specs, bytes.size(),
|
||||||
return write_padded(out, specs, bytes.size(), [bytes](iterator it) {
|
[bytes](reserve_iterator<OutputIt> it) {
|
||||||
const char* data = bytes.data();
|
const char* data = bytes.data();
|
||||||
return copy_str<Char>(data, data + bytes.size(), it);
|
return copy_str<Char>(data, data + bytes.size(), it);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Char, typename OutputIt>
|
template <typename Char, typename OutputIt>
|
||||||
constexpr OutputIt write_char(OutputIt out, Char value,
|
constexpr OutputIt write_char(OutputIt out, Char value,
|
||||||
const basic_format_specs<Char>& specs) {
|
const basic_format_specs<Char>& specs) {
|
||||||
using iterator = remove_reference_t<decltype(reserve(out, 0))>;
|
return write_padded(out, specs, 1, [=](reserve_iterator<OutputIt> it) {
|
||||||
return write_padded(out, specs, 1, [=](iterator it) {
|
|
||||||
*it++ = value;
|
*it++ = value;
|
||||||
return it;
|
return it;
|
||||||
});
|
});
|
||||||
@ -1747,9 +1664,9 @@ template <typename Char> struct write_int_data {
|
|||||||
size_t size;
|
size_t size;
|
||||||
size_t padding;
|
size_t padding;
|
||||||
|
|
||||||
FMT_CONSTEXPR write_int_data(int num_digits, string_view prefix,
|
FMT_CONSTEXPR write_int_data(int num_digits, unsigned prefix,
|
||||||
const basic_format_specs<Char>& specs)
|
const basic_format_specs<Char>& specs)
|
||||||
: size(prefix.size() + to_unsigned(num_digits)), padding(0) {
|
: size((prefix >> 24) + to_unsigned(num_digits)), padding(0) {
|
||||||
if (specs.align == align::numeric) {
|
if (specs.align == align::numeric) {
|
||||||
auto width = to_unsigned(specs.width);
|
auto width = to_unsigned(specs.width);
|
||||||
if (width > size) {
|
if (width > size) {
|
||||||
@ -1757,7 +1674,7 @@ template <typename Char> struct write_int_data {
|
|||||||
size = width;
|
size = width;
|
||||||
}
|
}
|
||||||
} else if (specs.precision > num_digits) {
|
} else if (specs.precision > num_digits) {
|
||||||
size = prefix.size() + to_unsigned(specs.precision);
|
size = (prefix >> 24) + to_unsigned(specs.precision);
|
||||||
padding = to_unsigned(specs.precision - num_digits);
|
padding = to_unsigned(specs.precision - num_digits);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1765,22 +1682,164 @@ template <typename Char> struct write_int_data {
|
|||||||
|
|
||||||
// Writes an integer in the format
|
// Writes an integer in the format
|
||||||
// <left-padding><prefix><numeric-padding><digits><right-padding>
|
// <left-padding><prefix><numeric-padding><digits><right-padding>
|
||||||
// where <digits> are written by f(it).
|
// where <digits> are written by write_digits(it).
|
||||||
template <typename OutputIt, typename Char, typename F>
|
// prefix contains chars in three lower bytes and the size in the fourth byte.
|
||||||
FMT_CONSTEXPR OutputIt write_int(OutputIt out, int num_digits,
|
template <typename OutputIt, typename Char, typename W>
|
||||||
string_view prefix,
|
FMT_CONSTEXPR FMT_INLINE OutputIt
|
||||||
const basic_format_specs<Char>& specs, F f) {
|
write_int(OutputIt out, int num_digits, unsigned prefix,
|
||||||
|
const basic_format_specs<Char>& specs, W write_digits) {
|
||||||
|
// Slightly faster check for specs.width == 0 && specs.precision == -1.
|
||||||
|
if ((specs.width | (specs.precision + 1)) == 0) {
|
||||||
|
auto it = reserve(out, to_unsigned(num_digits) + (prefix >> 24));
|
||||||
|
if (prefix != 0) {
|
||||||
|
for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8)
|
||||||
|
*it++ = static_cast<Char>(p & 0xff);
|
||||||
|
}
|
||||||
|
return base_iterator(out, write_digits(it));
|
||||||
|
}
|
||||||
auto data = write_int_data<Char>(num_digits, prefix, specs);
|
auto data = write_int_data<Char>(num_digits, prefix, specs);
|
||||||
using iterator = remove_reference_t<decltype(reserve(out, 0))>;
|
return write_padded<align::right>(
|
||||||
return write_padded<align::right>(out, specs, data.size, [=](iterator it) {
|
out, specs, data.size, [=](reserve_iterator<OutputIt> it) {
|
||||||
if (prefix.size() != 0)
|
for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8)
|
||||||
it = copy_str<Char>(prefix.begin(), prefix.end(), it);
|
*it++ = static_cast<Char>(p & 0xff);
|
||||||
it = detail::fill_n(it, data.padding, static_cast<Char>('0'));
|
it = detail::fill_n(it, data.padding, static_cast<Char>('0'));
|
||||||
return f(it);
|
return write_digits(it);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename StrChar, typename Char, typename OutputIt>
|
template <typename OutputIt, typename UInt, typename Char>
|
||||||
|
FMT_CONSTEXPR OutputIt write_dec(OutputIt out, UInt value, unsigned prefix,
|
||||||
|
const basic_format_specs<Char>& specs) {
|
||||||
|
auto num_digits = count_digits(value);
|
||||||
|
return write_int(out, num_digits, prefix, specs,
|
||||||
|
[=](reserve_iterator<OutputIt> it) {
|
||||||
|
return format_decimal<Char>(it, value, num_digits).end;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt, typename UInt, typename Char>
|
||||||
|
OutputIt write_int_localized(OutputIt out, UInt value, unsigned prefix,
|
||||||
|
const basic_format_specs<Char>& specs,
|
||||||
|
locale_ref loc) {
|
||||||
|
static_assert(std::is_same<uint64_or_128_t<UInt>, UInt>::value, "");
|
||||||
|
const auto sep_size = 1;
|
||||||
|
std::string groups = grouping<Char>(loc);
|
||||||
|
if (groups.empty()) return write_dec(out, value, prefix, specs);
|
||||||
|
auto sep = thousands_sep<Char>(loc);
|
||||||
|
if (!sep) return write_dec(out, value, prefix, specs);
|
||||||
|
int num_digits = count_digits(value);
|
||||||
|
int size = num_digits, n = num_digits;
|
||||||
|
std::string::const_iterator group = groups.cbegin();
|
||||||
|
while (group != groups.cend() && n > *group && *group > 0 &&
|
||||||
|
*group != max_value<char>()) {
|
||||||
|
size += sep_size;
|
||||||
|
n -= *group;
|
||||||
|
++group;
|
||||||
|
}
|
||||||
|
if (group == groups.cend()) size += sep_size * ((n - 1) / groups.back());
|
||||||
|
char digits[40];
|
||||||
|
format_decimal(digits, value, num_digits);
|
||||||
|
basic_memory_buffer<Char> buffer;
|
||||||
|
if (prefix != 0) ++size;
|
||||||
|
const auto usize = to_unsigned(size);
|
||||||
|
buffer.resize(usize);
|
||||||
|
basic_string_view<Char> s(&sep, sep_size);
|
||||||
|
// Index of a decimal digit with the least significant digit having index 0.
|
||||||
|
int digit_index = 0;
|
||||||
|
group = groups.cbegin();
|
||||||
|
auto p = buffer.data() + size - 1;
|
||||||
|
for (int i = num_digits - 1; i > 0; --i) {
|
||||||
|
*p-- = static_cast<Char>(digits[i]);
|
||||||
|
if (*group <= 0 || ++digit_index % *group != 0 ||
|
||||||
|
*group == max_value<char>())
|
||||||
|
continue;
|
||||||
|
if (group + 1 != groups.cend()) {
|
||||||
|
digit_index = 0;
|
||||||
|
++group;
|
||||||
|
}
|
||||||
|
std::uninitialized_copy(s.data(), s.data() + s.size(),
|
||||||
|
make_checked(p, s.size()));
|
||||||
|
p -= s.size();
|
||||||
|
}
|
||||||
|
*p-- = static_cast<Char>(*digits);
|
||||||
|
if (prefix != 0) *p = static_cast<Char>(prefix);
|
||||||
|
auto data = buffer.data();
|
||||||
|
return write_padded<align::right>(
|
||||||
|
out, specs, usize, usize, [=](reserve_iterator<OutputIt> it) {
|
||||||
|
return copy_str<Char>(data, data + size, it);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR inline void prefix_append(unsigned& prefix, unsigned value) {
|
||||||
|
prefix |= prefix != 0 ? value << 8 : value;
|
||||||
|
prefix += (1u + (value > 0xff ? 1 : 0)) << 24;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt, typename T, typename Char>
|
||||||
|
FMT_CONSTEXPR OutputIt write_int(OutputIt out, T value,
|
||||||
|
const basic_format_specs<Char>& specs,
|
||||||
|
locale_ref loc) {
|
||||||
|
auto prefix = 0u;
|
||||||
|
auto abs_value = static_cast<uint32_or_64_or_128_t<T>>(value);
|
||||||
|
if (is_negative(value)) {
|
||||||
|
prefix = 0x01000000 | '-';
|
||||||
|
abs_value = 0 - abs_value;
|
||||||
|
} else {
|
||||||
|
prefix = data::prefixes[specs.sign];
|
||||||
|
}
|
||||||
|
auto utype = static_cast<unsigned>(specs.type);
|
||||||
|
switch (specs.type) {
|
||||||
|
case 0:
|
||||||
|
case 'd':
|
||||||
|
return specs.localized
|
||||||
|
? write_int_localized(out,
|
||||||
|
static_cast<uint64_or_128_t<T>>(abs_value),
|
||||||
|
prefix, specs, loc)
|
||||||
|
: write_dec(out, abs_value, prefix, specs);
|
||||||
|
case 'x':
|
||||||
|
case 'X': {
|
||||||
|
if (specs.alt) prefix_append(prefix, (utype << 8) | '0');
|
||||||
|
bool upper = specs.type != 'x';
|
||||||
|
int num_digits = count_digits<4>(abs_value);
|
||||||
|
return write_int(
|
||||||
|
out, num_digits, prefix, specs, [=](reserve_iterator<OutputIt> it) {
|
||||||
|
return format_uint<4, Char>(it, abs_value, num_digits, upper);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
case 'b':
|
||||||
|
case 'B': {
|
||||||
|
if (specs.alt) prefix_append(prefix, (utype << 8) | '0');
|
||||||
|
int num_digits = count_digits<1>(abs_value);
|
||||||
|
return write_int(out, num_digits, prefix, specs,
|
||||||
|
[=](reserve_iterator<OutputIt> it) {
|
||||||
|
return format_uint<1, Char>(it, abs_value, num_digits);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
case 'o': {
|
||||||
|
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_append(prefix, '0');
|
||||||
|
}
|
||||||
|
return write_int(out, num_digits, prefix, specs,
|
||||||
|
[=](reserve_iterator<OutputIt> it) {
|
||||||
|
return format_uint<3, Char>(it, abs_value, num_digits);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
#ifdef FMT_DEPRECATED_N_SPECIFIER
|
||||||
|
case 'n':
|
||||||
|
return write_int_localized(out, abs_value, prefix, specs, loc);
|
||||||
|
#endif
|
||||||
|
case 'c':
|
||||||
|
return write_char(out, static_cast<Char>(abs_value), specs);
|
||||||
|
default:
|
||||||
|
FMT_THROW(format_error("invalid type specifier"));
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt, typename StrChar, typename Char>
|
||||||
FMT_CONSTEXPR OutputIt write(OutputIt out, basic_string_view<StrChar> s,
|
FMT_CONSTEXPR OutputIt write(OutputIt out, basic_string_view<StrChar> s,
|
||||||
const basic_format_specs<Char>& specs) {
|
const basic_format_specs<Char>& specs) {
|
||||||
auto data = s.data();
|
auto data = s.data();
|
||||||
@ -1790,154 +1849,12 @@ FMT_CONSTEXPR OutputIt write(OutputIt out, basic_string_view<StrChar> s,
|
|||||||
auto width = specs.width != 0
|
auto width = specs.width != 0
|
||||||
? compute_width(basic_string_view<StrChar>(data, size))
|
? compute_width(basic_string_view<StrChar>(data, size))
|
||||||
: 0;
|
: 0;
|
||||||
using iterator = remove_reference_t<decltype(reserve(out, 0))>;
|
return write_padded(out, specs, size, width,
|
||||||
return write_padded(out, specs, size, width, [=](iterator it) {
|
[=](reserve_iterator<OutputIt> it) {
|
||||||
return copy_str<Char>(data, data + size, it);
|
return copy_str<Char>(data, data + size, it);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// The handle_int_type_spec handler that writes an integer.
|
|
||||||
template <typename OutputIt, typename Char, typename UInt> struct int_writer {
|
|
||||||
OutputIt out;
|
|
||||||
locale_ref locale;
|
|
||||||
const basic_format_specs<Char>& specs;
|
|
||||||
UInt abs_value;
|
|
||||||
char prefix[4];
|
|
||||||
unsigned prefix_size;
|
|
||||||
|
|
||||||
using iterator =
|
|
||||||
remove_reference_t<decltype(reserve(std::declval<OutputIt&>(), 0))>;
|
|
||||||
|
|
||||||
constexpr string_view get_prefix() const {
|
|
||||||
return string_view(prefix, prefix_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR void write_dec() {
|
|
||||||
auto num_digits = count_digits(abs_value);
|
|
||||||
out = write_int(
|
|
||||||
out, num_digits, get_prefix(), specs, [this, num_digits](iterator it) {
|
|
||||||
return format_decimal<Char>(it, abs_value, num_digits).end;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Int>
|
|
||||||
FMT_CONSTEXPR int_writer(OutputIt output, locale_ref loc, Int value,
|
|
||||||
const basic_format_specs<Char>& s)
|
|
||||||
: out(output),
|
|
||||||
locale(loc),
|
|
||||||
specs(s),
|
|
||||||
abs_value(static_cast<UInt>(value)),
|
|
||||||
prefix_size(0) {
|
|
||||||
static_assert(std::is_same<uint32_or_64_or_128_t<Int>, UInt>::value, "");
|
|
||||||
if (is_negative(value)) {
|
|
||||||
prefix[0] = '-';
|
|
||||||
++prefix_size;
|
|
||||||
abs_value = 0 - abs_value;
|
|
||||||
} else if (specs.sign != sign::none && specs.sign != sign::minus) {
|
|
||||||
prefix[0] = specs.sign == sign::plus ? '+' : ' ';
|
|
||||||
++prefix_size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR void on_dec() {
|
|
||||||
if (specs.localized) return on_num();
|
|
||||||
write_dec();
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR void on_hex() {
|
|
||||||
if (specs.alt) {
|
|
||||||
prefix[prefix_size++] = '0';
|
|
||||||
prefix[prefix_size++] = specs.type;
|
|
||||||
}
|
|
||||||
int num_digits = count_digits<4>(abs_value);
|
|
||||||
out = write_int(out, num_digits, get_prefix(), specs,
|
|
||||||
[this, num_digits](iterator it) {
|
|
||||||
return format_uint<4, Char>(it, abs_value, num_digits,
|
|
||||||
specs.type != 'x');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR void on_bin() {
|
|
||||||
if (specs.alt) {
|
|
||||||
prefix[prefix_size++] = '0';
|
|
||||||
prefix[prefix_size++] = static_cast<char>(specs.type);
|
|
||||||
}
|
|
||||||
int num_digits = count_digits<1>(abs_value);
|
|
||||||
out = write_int(out, num_digits, get_prefix(), specs,
|
|
||||||
[this, num_digits](iterator it) {
|
|
||||||
return format_uint<1, Char>(it, abs_value, num_digits);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR void on_oct() {
|
|
||||||
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';
|
|
||||||
}
|
|
||||||
out = write_int(out, num_digits, get_prefix(), specs,
|
|
||||||
[this, num_digits](iterator it) {
|
|
||||||
return format_uint<3, Char>(it, abs_value, num_digits);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
enum { sep_size = 1 };
|
|
||||||
|
|
||||||
void on_num() {
|
|
||||||
std::string groups = grouping<Char>(locale);
|
|
||||||
if (groups.empty()) return write_dec();
|
|
||||||
auto sep = thousands_sep<Char>(locale);
|
|
||||||
if (!sep) return write_dec();
|
|
||||||
int num_digits = count_digits(abs_value);
|
|
||||||
int size = num_digits, n = num_digits;
|
|
||||||
std::string::const_iterator group = groups.cbegin();
|
|
||||||
while (group != groups.cend() && n > *group && *group > 0 &&
|
|
||||||
*group != max_value<char>()) {
|
|
||||||
size += sep_size;
|
|
||||||
n -= *group;
|
|
||||||
++group;
|
|
||||||
}
|
|
||||||
if (group == groups.cend()) size += sep_size * ((n - 1) / groups.back());
|
|
||||||
char digits[40];
|
|
||||||
format_decimal(digits, abs_value, num_digits);
|
|
||||||
basic_memory_buffer<Char> buffer;
|
|
||||||
size += static_cast<int>(prefix_size);
|
|
||||||
const auto usize = to_unsigned(size);
|
|
||||||
buffer.resize(usize);
|
|
||||||
basic_string_view<Char> s(&sep, sep_size);
|
|
||||||
// Index of a decimal digit with the least significant digit having index 0.
|
|
||||||
int digit_index = 0;
|
|
||||||
group = groups.cbegin();
|
|
||||||
auto p = buffer.data() + size - 1;
|
|
||||||
for (int i = num_digits - 1; i > 0; --i) {
|
|
||||||
*p-- = static_cast<Char>(digits[i]);
|
|
||||||
if (*group <= 0 || ++digit_index % *group != 0 ||
|
|
||||||
*group == max_value<char>())
|
|
||||||
continue;
|
|
||||||
if (group + 1 != groups.cend()) {
|
|
||||||
digit_index = 0;
|
|
||||||
++group;
|
|
||||||
}
|
|
||||||
std::uninitialized_copy(s.data(), s.data() + s.size(),
|
|
||||||
make_checked(p, s.size()));
|
|
||||||
p -= s.size();
|
|
||||||
}
|
|
||||||
*p-- = static_cast<Char>(*digits);
|
|
||||||
if (prefix_size != 0) *p = static_cast<Char>(prefix[0]);
|
|
||||||
auto data = buffer.data();
|
|
||||||
out = write_padded<align::right>(
|
|
||||||
out, specs, usize, usize,
|
|
||||||
[=](iterator it) { return copy_str<Char>(data, data + size, it); });
|
|
||||||
}
|
|
||||||
|
|
||||||
void on_chr() { out = write_char(out, static_cast<Char>(abs_value), specs); }
|
|
||||||
|
|
||||||
FMT_NORETURN void on_error() {
|
|
||||||
FMT_THROW(format_error("invalid type specifier"));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Char, typename OutputIt>
|
template <typename Char, typename OutputIt>
|
||||||
OutputIt write_nonfinite(OutputIt out, bool isinf,
|
OutputIt write_nonfinite(OutputIt out, bool isinf,
|
||||||
const basic_format_specs<Char>& specs,
|
const basic_format_specs<Char>& specs,
|
||||||
@ -1947,8 +1864,7 @@ OutputIt write_nonfinite(OutputIt out, bool isinf,
|
|||||||
constexpr size_t str_size = 3;
|
constexpr size_t str_size = 3;
|
||||||
auto sign = fspecs.sign;
|
auto sign = fspecs.sign;
|
||||||
auto size = str_size + (sign ? 1 : 0);
|
auto size = str_size + (sign ? 1 : 0);
|
||||||
using iterator = remove_reference_t<decltype(reserve(out, 0))>;
|
return write_padded(out, specs, size, [=](reserve_iterator<OutputIt> it) {
|
||||||
return write_padded(out, specs, size, [=](iterator it) {
|
|
||||||
if (sign) *it++ = static_cast<Char>(data::signs[sign]);
|
if (sign) *it++ = static_cast<Char>(data::signs[sign]);
|
||||||
return copy_str<Char>(str, str + str_size, it);
|
return copy_str<Char>(str, str + str_size, it);
|
||||||
});
|
});
|
||||||
@ -2028,7 +1944,7 @@ OutputIt write_float(OutputIt out, const DecimalFP& fp,
|
|||||||
static const Char zero = static_cast<Char>('0');
|
static const Char zero = static_cast<Char>('0');
|
||||||
auto sign = fspecs.sign;
|
auto sign = fspecs.sign;
|
||||||
size_t size = to_unsigned(significand_size) + (sign ? 1 : 0);
|
size_t size = to_unsigned(significand_size) + (sign ? 1 : 0);
|
||||||
using iterator = remove_reference_t<decltype(reserve(out, 0))>;
|
using iterator = reserve_iterator<OutputIt>;
|
||||||
|
|
||||||
int output_exp = fp.exponent + significand_size - 1;
|
int output_exp = fp.exponent + significand_size - 1;
|
||||||
auto use_exp_format = [=]() {
|
auto use_exp_format = [=]() {
|
||||||
@ -2203,8 +2119,7 @@ OutputIt write_ptr(OutputIt out, UIntPtr value,
|
|||||||
const basic_format_specs<Char>* specs) {
|
const basic_format_specs<Char>* specs) {
|
||||||
int num_digits = count_digits<4>(value);
|
int num_digits = count_digits<4>(value);
|
||||||
auto size = to_unsigned(num_digits) + size_t(2);
|
auto size = to_unsigned(num_digits) + size_t(2);
|
||||||
using iterator = remove_reference_t<decltype(reserve(out, 0))>;
|
auto write = [=](reserve_iterator<OutputIt> it) {
|
||||||
auto write = [=](iterator it) {
|
|
||||||
*it++ = static_cast<Char>('0');
|
*it++ = static_cast<Char>('0');
|
||||||
*it++ = static_cast<Char>('x');
|
*it++ = static_cast<Char>('x');
|
||||||
return format_uint<4, Char>(it, value, num_digits);
|
return format_uint<4, Char>(it, value, num_digits);
|
||||||
@ -2355,17 +2270,6 @@ class arg_formatter_base {
|
|||||||
return detail::reserve(out_, n);
|
return detail::reserve(out_, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
using reserve_iterator = remove_reference_t<decltype(
|
|
||||||
detail::reserve(std::declval<iterator&>(), 0))>;
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
FMT_CONSTEXPR void write_int(T value, const format_specs& spec) {
|
|
||||||
using uint_type = uint32_or_64_or_128_t<T>;
|
|
||||||
int_writer<iterator, Char, uint_type> w(out_, locale_, value, spec);
|
|
||||||
handle_int_type_spec(spec.type, w);
|
|
||||||
out_ = w.out;
|
|
||||||
}
|
|
||||||
|
|
||||||
void write(char value) {
|
void write(char value) {
|
||||||
auto&& it = reserve(1);
|
auto&& it = reserve(1);
|
||||||
*it++ = value;
|
*it++ = value;
|
||||||
@ -2390,9 +2294,10 @@ class arg_formatter_base {
|
|||||||
void write(const Ch* s, size_t size, const format_specs& specs) {
|
void write(const Ch* s, size_t size, const format_specs& specs) {
|
||||||
auto width =
|
auto width =
|
||||||
specs.width != 0 ? compute_width(basic_string_view<Ch>(s, size)) : 0;
|
specs.width != 0 ? compute_width(basic_string_view<Ch>(s, size)) : 0;
|
||||||
out_ = write_padded(out_, specs, size, width, [=](reserve_iterator it) {
|
out_ = write_padded(out_, specs, size, width,
|
||||||
return copy_str<Char>(s, s + size, it);
|
[=](reserve_iterator<OutputIt> it) {
|
||||||
});
|
return copy_str<Char>(s, s + size, it);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Ch>
|
template <typename Ch>
|
||||||
@ -2414,7 +2319,9 @@ class arg_formatter_base {
|
|||||||
|
|
||||||
FMT_CONSTEXPR void on_int() {
|
FMT_CONSTEXPR void on_int() {
|
||||||
// char is only formatted as int if there are specs.
|
// char is only formatted as int if there are specs.
|
||||||
formatter.write_int(static_cast<int>(value), *formatter.specs_);
|
formatter.out_ =
|
||||||
|
detail::write_int(formatter.out_, static_cast<int>(value),
|
||||||
|
*formatter.specs_, formatter.locale_);
|
||||||
}
|
}
|
||||||
FMT_CONSTEXPR void on_char() {
|
FMT_CONSTEXPR void on_char() {
|
||||||
if (formatter.specs_)
|
if (formatter.specs_)
|
||||||
@ -2467,11 +2374,8 @@ class arg_formatter_base {
|
|||||||
|
|
||||||
template <typename T, FMT_ENABLE_IF(is_integral<T>::value)>
|
template <typename T, FMT_ENABLE_IF(is_integral<T>::value)>
|
||||||
FMT_CONSTEXPR FMT_INLINE iterator operator()(T value) {
|
FMT_CONSTEXPR FMT_INLINE iterator operator()(T value) {
|
||||||
if (specs_)
|
return out_ = specs_ ? detail::write_int(out_, value, *specs_, locale_)
|
||||||
write_int(value, *specs_);
|
: detail::write<Char>(out_, value);
|
||||||
else
|
|
||||||
out_ = detail::write<Char>(out_, value);
|
|
||||||
return out_;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_CONSTEXPR iterator operator()(Char value) {
|
FMT_CONSTEXPR iterator operator()(Char value) {
|
||||||
@ -2529,8 +2433,6 @@ class arg_formatter : public arg_formatter_base<OutputIt, Char> {
|
|||||||
using context_type = basic_format_context<OutputIt, Char>;
|
using context_type = basic_format_context<OutputIt, Char>;
|
||||||
|
|
||||||
context_type& ctx_;
|
context_type& ctx_;
|
||||||
basic_format_parse_context<char_type>* parse_ctx_;
|
|
||||||
const Char* ptr_;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using iterator = typename base::iterator;
|
using iterator = typename base::iterator;
|
||||||
@ -2543,21 +2445,15 @@ class arg_formatter : public arg_formatter_base<OutputIt, Char> {
|
|||||||
*specs* contains format specifier information for standard argument types.
|
*specs* contains format specifier information for standard argument types.
|
||||||
\endrst
|
\endrst
|
||||||
*/
|
*/
|
||||||
constexpr explicit arg_formatter(
|
constexpr explicit arg_formatter(context_type& ctx,
|
||||||
context_type& ctx,
|
format_specs* specs = nullptr)
|
||||||
basic_format_parse_context<char_type>* parse_ctx = nullptr,
|
: base(ctx.out(), specs, ctx.locale()), ctx_(ctx) {}
|
||||||
format_specs* specs = nullptr, const Char* ptr = nullptr)
|
|
||||||
: base(ctx.out(), specs, ctx.locale()),
|
|
||||||
ctx_(ctx),
|
|
||||||
parse_ctx_(parse_ctx),
|
|
||||||
ptr_(ptr) {}
|
|
||||||
|
|
||||||
using base::operator();
|
using base::operator();
|
||||||
|
|
||||||
/** Formats an argument of a user-defined type. */
|
iterator operator()(typename basic_format_arg<context_type>::handle) {
|
||||||
iterator operator()(typename basic_format_arg<context_type>::handle handle) {
|
// User-defined types are handled separately because they require access to
|
||||||
if (ptr_) advance_to(*parse_ctx_, ptr_);
|
// the parse context.
|
||||||
handle.format(*parse_ctx_, ctx_);
|
|
||||||
return ctx_.out();
|
return ctx_.out();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -2926,14 +2822,10 @@ class dynamic_specs_handler
|
|||||||
};
|
};
|
||||||
|
|
||||||
template <typename Char, typename IDHandler>
|
template <typename Char, typename IDHandler>
|
||||||
FMT_CONSTEXPR const Char* parse_arg_id(const Char* begin, const Char* end,
|
FMT_CONSTEXPR const Char* do_parse_arg_id(const Char* begin, const Char* end,
|
||||||
IDHandler&& handler) {
|
IDHandler&& handler) {
|
||||||
FMT_ASSERT(begin != end, "");
|
FMT_ASSERT(begin != end, "");
|
||||||
Char c = *begin;
|
Char c = *begin;
|
||||||
if (c == '}' || c == ':') {
|
|
||||||
handler();
|
|
||||||
return begin;
|
|
||||||
}
|
|
||||||
if (c >= '0' && c <= '9') {
|
if (c >= '0' && c <= '9') {
|
||||||
int index = 0;
|
int index = 0;
|
||||||
if (c != '0')
|
if (c != '0')
|
||||||
@ -2958,6 +2850,16 @@ FMT_CONSTEXPR const Char* parse_arg_id(const Char* begin, const Char* end,
|
|||||||
return it;
|
return it;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename Char, typename IDHandler>
|
||||||
|
FMT_CONSTEXPR_DECL FMT_INLINE const Char* parse_arg_id(const Char* begin,
|
||||||
|
const Char* end,
|
||||||
|
IDHandler&& handler) {
|
||||||
|
Char c = *begin;
|
||||||
|
if (c != '}' && c != ':') return do_parse_arg_id(begin, end, handler);
|
||||||
|
handler();
|
||||||
|
return begin;
|
||||||
|
}
|
||||||
|
|
||||||
// Adapts SpecHandler to IDHandler API for dynamic width.
|
// Adapts SpecHandler to IDHandler API for dynamic width.
|
||||||
template <typename SpecHandler, typename Char> struct width_adapter {
|
template <typename SpecHandler, typename Char> struct width_adapter {
|
||||||
explicit FMT_CONSTEXPR width_adapter(SpecHandler& h) : handler(h) {}
|
explicit FMT_CONSTEXPR width_adapter(SpecHandler& h) : handler(h) {}
|
||||||
@ -3092,8 +2994,14 @@ FMT_CONSTEXPR const Char* parse_precision(const Char* begin, const Char* end,
|
|||||||
// Parses standard format specifiers and sends notifications about parsed
|
// Parses standard format specifiers and sends notifications about parsed
|
||||||
// components to handler.
|
// components to handler.
|
||||||
template <typename Char, typename SpecHandler>
|
template <typename Char, typename SpecHandler>
|
||||||
FMT_CONSTEXPR const Char* parse_format_specs(const Char* begin, const Char* end,
|
FMT_CONSTEXPR_DECL FMT_INLINE const Char* parse_format_specs(
|
||||||
SpecHandler&& handler) {
|
const Char* begin, const Char* end, SpecHandler&& handler) {
|
||||||
|
if (begin + 1 < end && begin[1] == '}' && is_ascii_letter(*begin) &&
|
||||||
|
*begin != 'L') {
|
||||||
|
handler.on_type(*begin++);
|
||||||
|
return begin;
|
||||||
|
}
|
||||||
|
|
||||||
if (begin == end) return begin;
|
if (begin == end) return begin;
|
||||||
|
|
||||||
begin = parse_align(begin, end, handler);
|
begin = parse_align(begin, end, handler);
|
||||||
@ -3306,21 +3214,14 @@ struct format_handler : detail::error_handler {
|
|||||||
return parse_context.begin();
|
return parse_context.begin();
|
||||||
}
|
}
|
||||||
auto specs = basic_format_specs<Char>();
|
auto specs = basic_format_specs<Char>();
|
||||||
if (begin + 1 < end && begin[1] == '}' && is_ascii_letter(*begin) &&
|
using parse_context_t = basic_format_parse_context<Char>;
|
||||||
*begin != 'L') {
|
specs_checker<specs_handler<parse_context_t, Context>> handler(
|
||||||
specs.type = static_cast<char>(*begin++);
|
specs_handler<parse_context_t, Context>(specs, parse_context, context),
|
||||||
} else {
|
arg.type());
|
||||||
using parse_context_t = basic_format_parse_context<Char>;
|
begin = parse_format_specs(begin, end, handler);
|
||||||
specs_checker<specs_handler<parse_context_t, Context>> handler(
|
if (begin == end || *begin != '}') on_error("missing '}' in format string");
|
||||||
specs_handler<parse_context_t, Context>(specs, parse_context,
|
context.advance_to(
|
||||||
context),
|
visit_format_arg(arg_formatter<OutputIt, Char>(context, &specs), arg));
|
||||||
arg.type());
|
|
||||||
begin = parse_format_specs(begin, end, handler);
|
|
||||||
if (begin == end || *begin != '}')
|
|
||||||
on_error("missing '}' in format string");
|
|
||||||
}
|
|
||||||
context.advance_to(visit_format_arg(
|
|
||||||
arg_formatter<OutputIt, Char>(context, &parse_context, &specs), arg));
|
|
||||||
return begin;
|
return begin;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -3697,7 +3598,7 @@ struct formatter<T, Char,
|
|||||||
specs.precision, specs.precision_ref, ctx);
|
specs.precision, specs.precision_ref, ctx);
|
||||||
using af = detail::arg_formatter<typename FormatContext::iterator,
|
using af = detail::arg_formatter<typename FormatContext::iterator,
|
||||||
typename FormatContext::char_type>;
|
typename FormatContext::char_type>;
|
||||||
return visit_format_arg(af(ctx, nullptr, &specs),
|
return visit_format_arg(af(ctx, &specs),
|
||||||
detail::make_arg<FormatContext>(val));
|
detail::make_arg<FormatContext>(val));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3800,8 +3701,7 @@ template <typename Char = char> class dynamic_formatter {
|
|||||||
if (specs_.precision >= 0) checker.end_precision();
|
if (specs_.precision >= 0) checker.end_precision();
|
||||||
using af = detail::arg_formatter<typename FormatContext::iterator,
|
using af = detail::arg_formatter<typename FormatContext::iterator,
|
||||||
typename FormatContext::char_type>;
|
typename FormatContext::char_type>;
|
||||||
visit_format_arg(af(ctx, nullptr, &specs_),
|
visit_format_arg(af(ctx, &specs_), detail::make_arg<FormatContext>(val));
|
||||||
detail::make_arg<FormatContext>(val));
|
|
||||||
return ctx.out();
|
return ctx.out();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -19,6 +19,42 @@
|
|||||||
#include "gtest-extra.h"
|
#include "gtest-extra.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
|
TEST(IteratorTest, TruncatingIterator) {
|
||||||
|
char* p = nullptr;
|
||||||
|
fmt::detail::truncating_iterator<char*> it(p, 3);
|
||||||
|
auto prev = it++;
|
||||||
|
EXPECT_EQ(prev.base(), p);
|
||||||
|
EXPECT_EQ(it.base(), p + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(IteratorTest, TruncatingIteratorDefaultConstruct) {
|
||||||
|
static_assert(std::is_default_constructible<
|
||||||
|
fmt::detail::truncating_iterator<char*>>::value,
|
||||||
|
"");
|
||||||
|
|
||||||
|
fmt::detail::truncating_iterator<char*> it;
|
||||||
|
EXPECT_EQ(nullptr, it.base());
|
||||||
|
EXPECT_EQ(std::size_t{0}, it.count());
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __cpp_lib_ranges
|
||||||
|
TEST(IteratorTest, TruncatingIteratorOutputIterator) {
|
||||||
|
static_assert(
|
||||||
|
std::output_iterator<fmt::detail::truncating_iterator<char*>, char>);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
TEST(IteratorTest, TruncatingBackInserter) {
|
||||||
|
std::string buffer;
|
||||||
|
auto bi = std::back_inserter(buffer);
|
||||||
|
fmt::detail::truncating_iterator<decltype(bi)> it(bi, 2);
|
||||||
|
*it++ = '4';
|
||||||
|
*it++ = '2';
|
||||||
|
*it++ = '1';
|
||||||
|
EXPECT_EQ(buffer.size(), 2);
|
||||||
|
EXPECT_EQ(buffer, "42");
|
||||||
|
}
|
||||||
|
|
||||||
// compiletime_prepared_parts_type_provider is useful only with relaxed
|
// compiletime_prepared_parts_type_provider is useful only with relaxed
|
||||||
// constexpr.
|
// constexpr.
|
||||||
#if FMT_USE_CONSTEXPR
|
#if FMT_USE_CONSTEXPR
|
||||||
@ -224,6 +260,11 @@ TEST(CompileTest, FormatToNWithCompileMacro) {
|
|||||||
EXPECT_STREQ("2a", buffer);
|
EXPECT_STREQ("2a", buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(CompileTest, FormattedSizeWithCompileMacro) {
|
||||||
|
EXPECT_EQ(2, fmt::formatted_size(FMT_COMPILE("{0}"), 42));
|
||||||
|
EXPECT_EQ(5, fmt::formatted_size(FMT_COMPILE("{0:<4.2f}"), 42.0));
|
||||||
|
}
|
||||||
|
|
||||||
TEST(CompileTest, TextAndArg) {
|
TEST(CompileTest, TextAndArg) {
|
||||||
EXPECT_EQ(">>>42<<<", fmt::format(FMT_COMPILE(">>>{}<<<"), 42));
|
EXPECT_EQ(">>>42<<<", fmt::format(FMT_COMPILE(">>>{}<<<"), 42));
|
||||||
EXPECT_EQ("42!", fmt::format(FMT_COMPILE("{}!"), 42));
|
EXPECT_EQ("42!", fmt::format(FMT_COMPILE("{}!"), 42));
|
||||||
|
|||||||
@ -151,43 +151,6 @@ TEST(IteratorTest, CountingIterator) {
|
|||||||
EXPECT_EQ((it + 41).count(), 42);
|
EXPECT_EQ((it + 41).count(), 42);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(IteratorTest, TruncatingIterator) {
|
|
||||||
char* p = nullptr;
|
|
||||||
fmt::detail::truncating_iterator<char*> it(p, 3);
|
|
||||||
auto prev = it++;
|
|
||||||
EXPECT_EQ(prev.base(), p);
|
|
||||||
EXPECT_EQ(it.base(), p + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TEST(IteratorTest, TruncatingIteratorDefaultConstruct) {
|
|
||||||
static_assert(
|
|
||||||
std::is_default_constructible<fmt::detail::truncating_iterator<char*>>::value,
|
|
||||||
"");
|
|
||||||
|
|
||||||
fmt::detail::truncating_iterator<char*> it;
|
|
||||||
EXPECT_EQ(nullptr, it.base());
|
|
||||||
EXPECT_EQ(std::size_t{0}, it.count());
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef __cpp_lib_ranges
|
|
||||||
TEST(IteratorTest, TruncatingIteratorOutputIterator) {
|
|
||||||
static_assert(std::output_iterator<fmt::detail::truncating_iterator<char*>,
|
|
||||||
char>);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
TEST(IteratorTest, TruncatingBackInserter) {
|
|
||||||
std::string buffer;
|
|
||||||
auto bi = std::back_inserter(buffer);
|
|
||||||
fmt::detail::truncating_iterator<decltype(bi)> it(bi, 2);
|
|
||||||
*it++ = '4';
|
|
||||||
*it++ = '2';
|
|
||||||
*it++ = '1';
|
|
||||||
EXPECT_EQ(buffer.size(), 2);
|
|
||||||
EXPECT_EQ(buffer, "42");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(IteratorTest, IsOutputIterator) {
|
TEST(IteratorTest, IsOutputIterator) {
|
||||||
EXPECT_TRUE((fmt::detail::is_output_iterator<char*, char>::value));
|
EXPECT_TRUE((fmt::detail::is_output_iterator<char*, char>::value));
|
||||||
EXPECT_FALSE((fmt::detail::is_output_iterator<const char*, char>::value));
|
EXPECT_FALSE((fmt::detail::is_output_iterator<const char*, char>::value));
|
||||||
|
|||||||
@ -64,26 +64,6 @@ TEST(OStreamTest, Enum) {
|
|||||||
EXPECT_EQ(L"0", fmt::format(L"{}", unstreamable_enum()));
|
EXPECT_EQ(L"0", fmt::format(L"{}", unstreamable_enum()));
|
||||||
}
|
}
|
||||||
|
|
||||||
struct test_arg_formatter
|
|
||||||
: fmt::detail::arg_formatter<fmt::format_context::iterator, char> {
|
|
||||||
fmt::format_parse_context parse_ctx;
|
|
||||||
test_arg_formatter(fmt::format_context& ctx, fmt::format_specs& s)
|
|
||||||
: fmt::detail::arg_formatter<fmt::format_context::iterator, char>(
|
|
||||||
ctx, &parse_ctx, &s),
|
|
||||||
parse_ctx("") {}
|
|
||||||
};
|
|
||||||
|
|
||||||
TEST(OStreamTest, CustomArg) {
|
|
||||||
fmt::memory_buffer buffer;
|
|
||||||
fmt::format_context ctx(fmt::detail::buffer_appender<char>{buffer},
|
|
||||||
fmt::format_args());
|
|
||||||
fmt::format_specs spec;
|
|
||||||
test_arg_formatter af(ctx, spec);
|
|
||||||
fmt::visit_format_arg(
|
|
||||||
af, fmt::detail::make_arg<fmt::format_context>(streamable_enum()));
|
|
||||||
EXPECT_EQ("streamable_enum", std::string(buffer.data(), buffer.size()));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(OStreamTest, Format) {
|
TEST(OStreamTest, Format) {
|
||||||
EXPECT_EQ("a string", format("{0}", TestString("a string")));
|
EXPECT_EQ("a string", format("{0}", TestString("a string")));
|
||||||
std::string s = format("The date is {0}", Date(2012, 12, 9));
|
std::string s = format("The date is {0}", Date(2012, 12, 9));
|
||||||
|
|||||||
@ -606,23 +606,3 @@ TEST(PrintfTest, VSPrintfMakeWArgsExample) {
|
|||||||
{fmt::make_wprintf_args(42, L"something")}));
|
{fmt::make_wprintf_args(42, L"something")}));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, PrintfDetermineOutputSize) {
|
|
||||||
using backit = std::back_insert_iterator<std::vector<char>>;
|
|
||||||
using truncated_printf_context =
|
|
||||||
fmt::basic_printf_context<fmt::detail::truncating_iterator<backit>, char>;
|
|
||||||
|
|
||||||
auto v = std::vector<char>{};
|
|
||||||
auto it = std::back_inserter(v);
|
|
||||||
|
|
||||||
const auto format_string = "%s";
|
|
||||||
const auto format_arg = "Hello";
|
|
||||||
const auto expected_size = fmt::sprintf(format_string, format_arg).size();
|
|
||||||
|
|
||||||
EXPECT_EQ((truncated_printf_context(
|
|
||||||
fmt::detail::truncating_iterator<backit>(it, 0), format_string,
|
|
||||||
fmt::make_format_args<truncated_printf_context>(format_arg))
|
|
||||||
.format()
|
|
||||||
.count()),
|
|
||||||
expected_size);
|
|
||||||
}
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user