Get rid of bit fields

This commit is contained in:
Victor Zverovich 2024-08-11 10:13:17 -07:00
parent f8c0c8ee78
commit b906c321f0
8 changed files with 300 additions and 225 deletions

View File

@ -2092,51 +2092,6 @@ using unsigned_char = typename conditional_t<std::is_integral<Char>::value,
std::make_unsigned<Char>,
type_identity<unsigned>>::type;
// Character (code unit) type is erased to prevent template bloat.
struct fill_t {
private:
enum { max_size = 4 };
char data_[max_size] = {' '};
unsigned char size_ = 1;
public:
template <typename Char>
FMT_CONSTEXPR void operator=(basic_string_view<Char> s) {
auto size = s.size();
size_ = static_cast<unsigned char>(size);
if (size == 1) {
unsigned uchar = static_cast<unsigned_char<Char>>(s[0]);
data_[0] = static_cast<char>(uchar);
data_[1] = static_cast<char>(uchar >> 8);
return;
}
FMT_ASSERT(size <= max_size, "invalid fill");
for (size_t i = 0; i < size; ++i) data_[i] = static_cast<char>(s[i]);
}
FMT_CONSTEXPR void operator=(char c) {
data_[0] = c;
size_ = 1;
}
constexpr auto size() const -> size_t { return size_; }
template <typename Char> constexpr auto get() const -> Char {
using uchar = unsigned char;
return static_cast<Char>(static_cast<uchar>(data_[0]) |
(static_cast<uchar>(data_[1]) << 8));
}
template <typename Char, FMT_ENABLE_IF(std::is_same<Char, char>::value)>
constexpr auto data() const -> const Char* {
return data_;
}
template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
constexpr auto data() const -> const Char* {
return nullptr;
}
};
enum class arg_id_kind { none, index, name };
} // namespace detail
@ -2164,39 +2119,156 @@ enum class presentation_type : unsigned char {
hexfloat // 'a' or 'A'
};
// Basic format specifiers for built-in and string types.
class basic_specs {
private:
// Upper 32-bit of data contain fill and lower 32-bit are arranged as follows:
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |type |align| w | p | s |u|#|L| f | unused |
// +-----+-----+---+---+---+-+-+-+-----+---------------------------+
//
// w - dynamic width info
// p - dynamic precision info
// s - sign
// u - uppercase (e.g. 'X' for 'x')
// # - alternate form ('#')
// L - localized
// f - fill size
//
// Bitfields are not used because of compiler bugs such as gcc bug 61414.
enum : unsigned {
type_mask = 0x00007,
align_mask = 0x00038,
width_mask = 0x000C0,
precision_mask = 0x00300,
sign_mask = 0x00C00,
uppercase_mask = 0x01000,
alternate_mask = 0x02000,
localized_mask = 0x04000,
fill_size_mask = 0x38000,
align_shift = 3,
width_shift = 6,
precision_shift = 8,
sign_shift = 10,
fill_size_shift = 15,
max_fill_size = 4
};
unsigned long long data_ = 1 << fill_size_shift;
// Character (code unit) type is erased to prevent template bloat.
char fill_data_[max_fill_size] = {' '};
FMT_CONSTEXPR void set_fill_size(size_t size) {
data_ = (data_ & ~fill_size_mask) | (size << fill_size_shift);
}
public:
constexpr auto type() const -> presentation_type {
return static_cast<presentation_type>(data_ & type_mask);
}
FMT_CONSTEXPR void set_type(presentation_type t) {
data_ = (data_ & ~type_mask) | static_cast<unsigned>(t);
}
constexpr auto align() const -> align_t {
return static_cast<align_t>((data_ & align_mask) >> align_shift);
}
FMT_CONSTEXPR void set_align(align_t a) {
data_ = (data_ & ~align_mask) | (static_cast<unsigned>(a) << align_shift);
}
constexpr auto dynamic_width() const -> detail::arg_id_kind {
return static_cast<detail::arg_id_kind>((data_ & width_mask) >>
width_shift);
}
FMT_CONSTEXPR void set_dynamic_width(detail::arg_id_kind w) {
data_ = (data_ & ~width_mask) | (static_cast<unsigned>(w) << width_shift);
}
FMT_CONSTEXPR auto dynamic_precision() const -> detail::arg_id_kind {
return static_cast<detail::arg_id_kind>((data_ & precision_mask) >>
precision_shift);
}
FMT_CONSTEXPR void set_dynamic_precision(detail::arg_id_kind p) {
data_ = (data_ & ~precision_mask) |
(static_cast<unsigned>(p) << precision_shift);
}
constexpr bool dynamic() const {
return (data_ & (width_mask | precision_mask)) != 0;
}
constexpr auto sign() const -> sign_t {
return static_cast<sign_t>((data_ & sign_mask) >> sign_shift);
}
FMT_CONSTEXPR void set_sign(sign_t a) {
data_ = (data_ & ~sign_mask) | (static_cast<unsigned>(a) << sign_shift);
}
constexpr auto upper() const -> bool { return (data_ & uppercase_mask) != 0; }
FMT_CONSTEXPR void set_upper() { data_ |= uppercase_mask; }
constexpr auto alt() const -> bool { return (data_ & alternate_mask) != 0; }
FMT_CONSTEXPR void set_alt() { data_ |= alternate_mask; }
FMT_CONSTEXPR void clear_alt() { data_ &= ~alternate_mask; }
constexpr auto localized() const -> bool {
return (data_ & localized_mask) != 0;
}
FMT_CONSTEXPR void set_localized() { data_ |= localized_mask; }
constexpr auto fill_size() const -> size_t {
return (data_ & fill_size_mask) >> fill_size_shift;
}
template <typename Char, FMT_ENABLE_IF(std::is_same<Char, char>::value)>
constexpr auto fill() const -> const Char* {
return fill_data_;
}
template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
constexpr auto fill() const -> const Char* {
return nullptr;
}
template <typename Char> constexpr auto fill_unit() const -> Char {
using uchar = unsigned char;
return static_cast<Char>(static_cast<uchar>(fill_data_[0]) |
(static_cast<uchar>(fill_data_[1]) << 8));
}
FMT_CONSTEXPR void set_fill(char c) {
fill_data_[0] = c;
set_fill_size(1);
}
template <typename Char>
FMT_CONSTEXPR void set_fill(basic_string_view<Char> s) {
auto size = s.size();
set_fill_size(size);
if (size == 1) {
unsigned uchar = static_cast<detail::unsigned_char<Char>>(s[0]);
fill_data_[0] = static_cast<char>(uchar);
fill_data_[1] = static_cast<char>(uchar >> 8);
return;
}
FMT_ASSERT(size <= max_fill_size, "invalid fill");
for (size_t i = 0; i < size; ++i)
fill_data_[i & 3] = static_cast<char>(s[i]);
}
};
// Format specifiers for built-in and string types.
struct format_specs {
struct format_specs : basic_specs {
int width;
int precision;
presentation_type type;
align_t align : 4;
sign_t sign : 3;
unsigned char dynamic : 4;
bool upper : 1; // An uppercase version e.g. 'X' for 'x'.
bool alt : 1; // Alternate form ('#').
bool localized : 1;
detail::fill_t fill;
constexpr format_specs()
: width(0),
precision(-1),
type(presentation_type::none),
align(align::none),
sign(sign::none),
dynamic(0),
upper(false),
alt(false),
localized(false) {}
FMT_CONSTEXPR auto dynamic_width() const -> detail::arg_id_kind {
enum { dynamic_width_mask = 3 };
return static_cast<detail::arg_id_kind>(dynamic & dynamic_width_mask);
}
FMT_CONSTEXPR auto dynamic_precision() const -> detail::arg_id_kind {
enum { dynamic_precision_mask = 12 };
return static_cast<detail::arg_id_kind>(
(dynamic & dynamic_precision_mask) >> 2);
}
constexpr format_specs() : width(0), precision(-1) {}
};
namespace detail {
@ -2381,7 +2453,7 @@ FMT_CONSTEXPR auto parse_width(const Char* begin, const Char* end,
basic_format_parse_context<Char>& ctx)
-> const Char* {
auto result = parse_dynamic_spec(begin, end, specs.width, width_ref, ctx);
specs.dynamic = static_cast<unsigned char>(result.kind) & 0x3u;
specs.set_dynamic_width(result.kind);
return result.end;
}
@ -2398,9 +2470,7 @@ FMT_CONSTEXPR auto parse_precision(const Char* begin, const Char* end,
}
auto result =
parse_dynamic_spec(begin, end, specs.precision, precision_ref, ctx);
auto kind_val = static_cast<unsigned char>(result.kind);
specs.dynamic =
static_cast<unsigned char>(specs.dynamic | (kind_val << 2)) & 0xfu;
specs.set_dynamic_precision(result.kind);
return result.end;
}
@ -2439,7 +2509,7 @@ FMT_CONSTEXPR auto parse_format_specs(const Char* begin, const Char* end,
FMT_CONSTEXPR auto operator()(pres pres_type, int set) -> const Char* {
if (!in(arg_type, set)) report_error("invalid format specifier");
specs.type = pres_type;
specs.set_type(pres_type);
return begin + 1;
}
} parse_presentation_type{begin, specs, arg_type};
@ -2450,13 +2520,13 @@ FMT_CONSTEXPR auto parse_format_specs(const Char* begin, const Char* end,
case '>':
case '^':
enter_state(state::align);
specs.align = parse_align(c);
specs.set_align(parse_align(c));
++begin;
break;
case '+':
FMT_FALLTHROUGH;
case ' ':
specs.sign = c == ' ' ? sign::space : sign::plus;
specs.set_sign(c == ' ' ? sign::space : sign::plus);
FMT_FALLTHROUGH;
case '-':
enter_state(state::sign, in(arg_type, sint_set | float_set));
@ -2464,17 +2534,17 @@ FMT_CONSTEXPR auto parse_format_specs(const Char* begin, const Char* end,
break;
case '#':
enter_state(state::hash, is_arithmetic_type(arg_type));
specs.alt = true;
specs.set_alt();
++begin;
break;
case '0':
enter_state(state::zero);
if (!is_arithmetic_type(arg_type))
report_error("format specifier requires numeric argument");
if (specs.align == align::none) {
if (specs.align() == align::none) {
// Ignore 0 if align is specified for compatibility with std::format.
specs.align = align::numeric;
specs.fill = '0';
specs.set_align(align::numeric);
specs.set_fill('0');
}
++begin;
break;
@ -2498,40 +2568,40 @@ FMT_CONSTEXPR auto parse_format_specs(const Char* begin, const Char* end,
break;
case 'L':
enter_state(state::locale, is_arithmetic_type(arg_type));
specs.localized = true;
specs.set_localized();
++begin;
break;
case 'd':
return parse_presentation_type(pres::dec, integral_set);
case 'X':
specs.upper = true;
specs.set_upper();
FMT_FALLTHROUGH;
case 'x':
return parse_presentation_type(pres::hex, integral_set);
case 'o':
return parse_presentation_type(pres::oct, integral_set);
case 'B':
specs.upper = true;
specs.set_upper();
FMT_FALLTHROUGH;
case 'b':
return parse_presentation_type(pres::bin, integral_set);
case 'E':
specs.upper = true;
specs.set_upper();
FMT_FALLTHROUGH;
case 'e':
return parse_presentation_type(pres::exp, float_set);
case 'F':
specs.upper = true;
specs.set_upper();
FMT_FALLTHROUGH;
case 'f':
return parse_presentation_type(pres::fixed, float_set);
case 'G':
specs.upper = true;
specs.set_upper();
FMT_FALLTHROUGH;
case 'g':
return parse_presentation_type(pres::general, float_set);
case 'A':
specs.upper = true;
specs.set_upper();
FMT_FALLTHROUGH;
case 'a':
return parse_presentation_type(pres::hexfloat, float_set);
@ -2562,9 +2632,9 @@ FMT_CONSTEXPR auto parse_format_specs(const Char* begin, const Char* end,
}
auto align = parse_align(to_ascii(*fill_end));
enter_state(state::align, align != align::none);
specs.fill =
basic_string_view<Char>(begin, to_unsigned(fill_end - begin));
specs.align = align;
specs.set_fill(
basic_string_view<Char>(begin, to_unsigned(fill_end - begin)));
specs.set_align(align);
begin = fill_end + 1;
}
}
@ -2706,13 +2776,15 @@ FMT_CONSTEXPR auto parse_format_specs(ParseContext& ctx)
// Checks char specs and returns true iff the presentation type is char-like.
FMT_CONSTEXPR inline auto check_char_specs(const format_specs& specs) -> bool {
if (specs.type != presentation_type::none &&
specs.type != presentation_type::chr &&
specs.type != presentation_type::debug) {
auto type = specs.type();
if (type != presentation_type::none && type != presentation_type::chr &&
type != presentation_type::debug) {
return false;
}
if (specs.align == align::numeric || specs.sign != sign::none || specs.alt)
if (specs.align() == align::numeric || specs.sign() != sign::none ||
specs.alt()) {
report_error("invalid format specifier for char");
}
return true;
}
@ -2861,7 +2933,7 @@ template <typename T, typename Char, type TYPE> struct native_formatter {
FMT_ENABLE_IF(U == type::string_type || U == type::cstring_type ||
U == type::char_type)>
FMT_CONSTEXPR void set_debug_format(bool set = true) {
specs_.type = set ? presentation_type::debug : presentation_type::none;
specs_.set_type(set ? presentation_type::debug : presentation_type::none);
}
template <typename FormatContext>

View File

@ -1732,8 +1732,8 @@ template <typename Char, typename Rep, typename OutputIt,
auto format_duration_value(OutputIt out, Rep val, int precision) -> OutputIt {
auto specs = format_specs();
specs.precision = precision;
specs.type =
precision >= 0 ? presentation_type::fixed : presentation_type::general;
specs.set_type(precision >= 0 ? presentation_type::fixed
: presentation_type::general);
return write<Char>(out, val, specs);
}

View File

@ -1718,11 +1718,11 @@ constexpr auto convert_float(T value) -> convert_float_result<T> {
}
template <typename Char, typename OutputIt>
FMT_NOINLINE FMT_CONSTEXPR auto fill(OutputIt it, size_t n, const fill_t& fill)
-> OutputIt {
auto fill_size = fill.size();
if (fill_size == 1) return detail::fill_n(it, n, fill.template get<Char>());
if (const Char* data = fill.template data<Char>()) {
FMT_NOINLINE FMT_CONSTEXPR auto fill(OutputIt it, size_t n,
const basic_specs& specs) -> OutputIt {
auto fill_size = specs.fill_size();
if (fill_size == 1) return detail::fill_n(it, n, specs.fill_unit<Char>());
if (const Char* data = specs.fill<Char>()) {
for (size_t i = 0; i < n; ++i) it = copy<Char>(data, data + fill_size, it);
}
return it;
@ -1741,12 +1741,12 @@ FMT_CONSTEXPR auto write_padded(OutputIt out, const format_specs& specs,
// Shifts are encoded as string literals because static constexpr is not
// supported in constexpr functions.
auto* shifts = align == align::left ? "\x1f\x1f\x00\x01" : "\x00\x1f\x00\x01";
size_t 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());
if (left_padding != 0) it = fill<Char>(it, left_padding, specs.fill);
auto it = reserve(out, size + padding * specs.fill_size());
if (left_padding != 0) it = fill<Char>(it, left_padding, specs);
it = f(it);
if (right_padding != 0) it = fill<Char>(it, right_padding, specs.fill);
if (right_padding != 0) it = fill<Char>(it, right_padding, specs);
return base_iterator(out, it);
}
@ -1931,7 +1931,7 @@ auto write_escaped_char(OutputIt out, Char v) -> OutputIt {
template <typename Char, typename OutputIt>
FMT_CONSTEXPR auto write_char(OutputIt out, Char value,
const format_specs& specs) -> OutputIt {
bool is_debug = specs.type == presentation_type::debug;
bool is_debug = specs.type() == presentation_type::debug;
return write_padded<Char>(out, specs, 1, [=](reserve_iterator<OutputIt> it) {
if (is_debug) return write_escaped_char(it, value);
*it++ = value;
@ -1958,7 +1958,7 @@ template <typename Char> struct write_int_data {
FMT_CONSTEXPR write_int_data(int num_digits, unsigned prefix,
const format_specs& specs)
: 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);
if (width > size) {
padding = width - size;
@ -2076,7 +2076,7 @@ auto write_int(OutputIt out, UInt value, unsigned prefix,
static_assert(std::is_same<uint64_or_128_t<UInt>, UInt>::value, "");
int num_digits = 0;
auto buffer = memory_buffer();
switch (specs.type) {
switch (specs.type()) {
default:
FMT_ASSERT(false, "");
FMT_FALLTHROUGH;
@ -2086,22 +2086,22 @@ auto write_int(OutputIt out, UInt value, unsigned prefix,
format_decimal<char>(appender(buffer), value, num_digits);
break;
case presentation_type::hex:
if (specs.alt)
prefix_append(prefix, unsigned(specs.upper ? 'X' : 'x') << 8 | '0');
if (specs.alt())
prefix_append(prefix, unsigned(specs.upper() ? 'X' : 'x') << 8 | '0');
num_digits = count_digits<4>(value);
format_uint<4, char>(appender(buffer), value, num_digits, specs.upper);
format_uint<4, char>(appender(buffer), value, num_digits, specs.upper());
break;
case presentation_type::oct:
num_digits = count_digits<3>(value);
// Octal prefix '0' is counted as a digit, so only add it if precision
// is not greater than the number of digits.
if (specs.alt && specs.precision <= num_digits && value != 0)
if (specs.alt() && specs.precision <= num_digits && value != 0)
prefix_append(prefix, '0');
format_uint<3, char>(appender(buffer), value, num_digits);
break;
case presentation_type::bin:
if (specs.alt)
prefix_append(prefix, unsigned(specs.upper ? 'B' : 'b') << 8 | '0');
if (specs.alt())
prefix_append(prefix, unsigned(specs.upper() ? 'B' : 'b') << 8 | '0');
num_digits = count_digits<1>(value);
format_uint<1, char>(appender(buffer), value, num_digits);
break;
@ -2158,7 +2158,7 @@ template <typename Char = char> struct loc_writer {
template <typename T, FMT_ENABLE_IF(is_integer<T>::value)>
auto operator()(T value) -> bool {
auto arg = make_write_int_arg(value, specs.sign);
auto arg = make_write_int_arg(value, specs.sign());
write_int(out, static_cast<uint64_or_128_t<T>>(arg.abs_value), arg.prefix,
specs, digit_grouping<Char>(grouping, sep));
return true;
@ -2177,7 +2177,7 @@ FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, write_int_arg<T> arg,
static_assert(std::is_same<T, uint32_or_64_or_128_t<T>>::value, "");
auto abs_value = arg.abs_value;
auto prefix = arg.prefix;
switch (specs.type) {
switch (specs.type()) {
default:
FMT_ASSERT(false, "");
FMT_FALLTHROUGH;
@ -2190,19 +2190,19 @@ FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, write_int_arg<T> arg,
});
}
case presentation_type::hex: {
if (specs.alt)
prefix_append(prefix, unsigned(specs.upper ? 'X' : 'x') << 8 | '0');
if (specs.alt())
prefix_append(prefix, unsigned(specs.upper() ? 'X' : 'x') << 8 | '0');
int num_digits = count_digits<4>(abs_value);
return write_int<Char>(
out, num_digits, prefix, specs, [=](reserve_iterator<OutputIt> it) {
return format_uint<4, Char>(it, abs_value, num_digits, specs.upper);
return format_uint<4, Char>(it, abs_value, num_digits, specs.upper());
});
}
case presentation_type::oct: {
int num_digits = count_digits<3>(abs_value);
// Octal prefix '0' is counted as a digit, so only add it if precision
// is not greater than the number of digits.
if (specs.alt && specs.precision <= num_digits && abs_value != 0)
if (specs.alt() && specs.precision <= num_digits && abs_value != 0)
prefix_append(prefix, '0');
return write_int<Char>(
out, num_digits, prefix, specs, [=](reserve_iterator<OutputIt> it) {
@ -2210,8 +2210,8 @@ FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, write_int_arg<T> arg,
});
}
case presentation_type::bin: {
if (specs.alt)
prefix_append(prefix, unsigned(specs.upper ? 'B' : 'b') << 8 | '0');
if (specs.alt())
prefix_append(prefix, unsigned(specs.upper() ? 'B' : 'b') << 8 | '0');
int num_digits = count_digits<1>(abs_value);
return write_int<Char>(
out, num_digits, prefix, specs, [=](reserve_iterator<OutputIt> it) {
@ -2236,8 +2236,8 @@ template <typename Char, typename T,
FMT_CONSTEXPR FMT_INLINE auto write(basic_appender<Char> out, T value,
const format_specs& specs, locale_ref loc)
-> basic_appender<Char> {
if (specs.localized && write_loc(out, value, specs, loc)) return out;
return write_int_noinline<Char>(out, make_write_int_arg(value, specs.sign),
if (specs.localized() && write_loc(out, value, specs, loc)) return out;
return write_int_noinline<Char>(out, make_write_int_arg(value, specs.sign()),
specs, loc);
}
// An inlined version of write used in format string compilation.
@ -2249,8 +2249,8 @@ template <typename Char, typename OutputIt, typename T,
FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value,
const format_specs& specs, locale_ref loc)
-> OutputIt {
if (specs.localized && write_loc(out, value, specs, loc)) return out;
return write_int<Char>(out, make_write_int_arg(value, specs.sign), specs,
if (specs.localized() && write_loc(out, value, specs, loc)) return out;
return write_int<Char>(out, make_write_int_arg(value, specs.sign()), specs,
loc);
}
@ -2261,7 +2261,7 @@ FMT_CONSTEXPR auto write(OutputIt out, basic_string_view<Char> s,
auto size = s.size();
if (specs.precision >= 0 && to_unsigned(specs.precision) < size)
size = code_point_index(s, to_unsigned(specs.precision));
bool is_debug = specs.type == presentation_type::debug;
bool is_debug = specs.type() == presentation_type::debug;
size_t width = 0;
if (is_debug) {
@ -2291,7 +2291,7 @@ FMT_CONSTEXPR auto write(OutputIt out,
template <typename Char, typename OutputIt>
FMT_CONSTEXPR auto write(OutputIt out, const Char* s, const format_specs& specs,
locale_ref) -> OutputIt {
if (specs.type == presentation_type::pointer)
if (specs.type() == presentation_type::pointer)
return write_ptr<Char>(out, bit_cast<uintptr_t>(s), &specs);
if (!s) report_error("string pointer is null");
return write<Char>(out, basic_string_view<Char>(s), specs, {});
@ -2344,7 +2344,7 @@ FMT_CONSTEXPR auto parse_align(const Char* begin, const Char* end,
report_error("invalid fill character '{'");
return begin;
}
specs.fill = basic_string_view<Char>(begin, to_unsigned(p - begin));
specs.set_fill(basic_string_view<Char>(begin, to_unsigned(p - begin)));
begin = p + 1;
} else {
++begin;
@ -2355,7 +2355,7 @@ FMT_CONSTEXPR auto parse_align(const Char* begin, const Char* end,
}
p = begin;
}
specs.align = align;
specs.set_align(align);
return begin;
}
@ -2364,13 +2364,13 @@ FMT_CONSTEXPR20 auto write_nonfinite(OutputIt out, bool isnan,
format_specs specs, sign_t sign)
-> OutputIt {
auto str =
isnan ? (specs.upper ? "NAN" : "nan") : (specs.upper ? "INF" : "inf");
isnan ? (specs.upper() ? "NAN" : "nan") : (specs.upper() ? "INF" : "inf");
constexpr size_t str_size = 3;
auto size = str_size + (sign ? 1 : 0);
// Replace '0'-padding with space for non-finite values.
const bool is_zero_fill =
specs.fill.size() == 1 && specs.fill.template get<Char>() == '0';
if (is_zero_fill) specs.fill = ' ';
specs.fill_size() == 1 && specs.fill_unit<Char>() == '0';
if (is_zero_fill) specs.set_fill(' ');
return write_padded<Char>(out, specs, size,
[=](reserve_iterator<OutputIt> it) {
if (sign) *it++ = detail::sign<Char>(sign);
@ -2492,13 +2492,13 @@ FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f,
size_t size = to_unsigned(significand_size) + (sign ? 1 : 0);
using iterator = reserve_iterator<OutputIt>;
Char decimal_point = specs.localized ? detail::decimal_point<Char>(loc)
: static_cast<Char>('.');
Char decimal_point = specs.localized() ? detail::decimal_point<Char>(loc)
: static_cast<Char>('.');
int output_exp = f.exponent + significand_size - 1;
auto use_exp_format = [=]() {
if (specs.type == presentation_type::exp) return true;
if (specs.type == presentation_type::fixed) return false;
if (specs.type() == presentation_type::exp) return true;
if (specs.type() == presentation_type::fixed) return false;
// Use the fixed notation if the exponent is in [exp_lower, exp_upper),
// e.g. 0.0001 instead of 1e-04. Otherwise use the exponent notation.
const int exp_lower = -4, exp_upper = 16;
@ -2507,7 +2507,7 @@ FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f,
};
if (use_exp_format()) {
int num_zeros = 0;
if (specs.alt) {
if (specs.alt()) {
num_zeros = specs.precision - significand_size;
if (num_zeros < 0) num_zeros = 0;
size += to_unsigned(num_zeros);
@ -2519,7 +2519,7 @@ FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f,
if (abs_output_exp >= 100) exp_digits = abs_output_exp >= 1000 ? 4 : 3;
size += to_unsigned((decimal_point ? 1 : 0) + 2 + exp_digits);
char exp_char = specs.upper ? 'E' : 'e';
char exp_char = specs.upper() ? 'E' : 'e';
auto write = [=](iterator it) {
if (sign) *it++ = detail::sign<Char>(sign);
// Insert a decimal point after the first digit and add an exponent.
@ -2540,27 +2540,27 @@ FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f,
size += to_unsigned(f.exponent);
int num_zeros = specs.precision - exp;
abort_fuzzing_if(num_zeros > 5000);
if (specs.alt) {
if (specs.alt()) {
++size;
if (num_zeros <= 0 && specs.type != presentation_type::fixed)
if (num_zeros <= 0 && specs.type() != presentation_type::fixed)
num_zeros = 0;
if (num_zeros > 0) size += to_unsigned(num_zeros);
}
auto grouping = Grouping(loc, specs.localized);
auto grouping = Grouping(loc, specs.localized());
size += to_unsigned(grouping.count_separators(exp));
return write_padded<Char, align::right>(out, specs, size, [&](iterator it) {
if (sign) *it++ = detail::sign<Char>(sign);
it = write_significand<Char>(it, significand, significand_size,
f.exponent, grouping);
if (!specs.alt) return it;
if (!specs.alt()) return it;
*it++ = decimal_point;
return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it;
});
} else if (exp > 0) {
// 1234e-2 -> 12.34[0+]
int num_zeros = specs.alt ? specs.precision - significand_size : 0;
int num_zeros = specs.alt() ? specs.precision - significand_size : 0;
size += 1 + to_unsigned(num_zeros > 0 ? num_zeros : 0);
auto grouping = Grouping(loc, specs.localized);
auto grouping = Grouping(loc, specs.localized());
size += to_unsigned(grouping.count_separators(exp));
return write_padded<Char, align::right>(out, specs, size, [&](iterator it) {
if (sign) *it++ = detail::sign<Char>(sign);
@ -2575,7 +2575,7 @@ FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f,
specs.precision < num_zeros) {
num_zeros = specs.precision;
}
bool pointy = num_zeros != 0 || significand_size != 0 || specs.alt;
bool pointy = num_zeros != 0 || significand_size != 0 || specs.alt();
size += 1 + (pointy ? 1 : 0) + to_unsigned(num_zeros);
return write_padded<Char, align::right>(out, specs, size, [&](iterator it) {
if (sign) *it++ = detail::sign<Char>(sign);
@ -3116,20 +3116,20 @@ FMT_CONSTEXPR20 void format_hexfloat(Float value, format_specs specs,
char xdigits[num_bits<carrier_uint>() / 4];
detail::fill_n(xdigits, sizeof(xdigits), '0');
format_uint<4>(xdigits, f.f, num_xdigits, specs.upper);
format_uint<4>(xdigits, f.f, num_xdigits, specs.upper());
// Remove zero tail
while (print_xdigits > 0 && xdigits[print_xdigits] == '0') --print_xdigits;
buf.push_back('0');
buf.push_back(specs.upper ? 'X' : 'x');
buf.push_back(specs.upper() ? 'X' : 'x');
buf.push_back(xdigits[0]);
if (specs.alt || print_xdigits > 0 || print_xdigits < specs.precision)
if (specs.alt() || print_xdigits > 0 || print_xdigits < specs.precision)
buf.push_back('.');
buf.append(xdigits + 1, xdigits + 1 + print_xdigits);
for (; print_xdigits < specs.precision; ++print_xdigits) buf.push_back('0');
buf.push_back(specs.upper ? 'P' : 'p');
buf.push_back(specs.upper() ? 'P' : 'p');
uint32_t abs_e;
if (f.e < 0) {
@ -3167,7 +3167,7 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision,
static_assert(!std::is_same<Float, float>::value, "");
auto converted_value = convert_float(value);
const bool fixed = specs.type == presentation_type::fixed;
const bool fixed = specs.type() == presentation_type::fixed;
if (value == 0) {
if (precision <= 0 || !fixed) {
buf.push_back('0');
@ -3446,7 +3446,7 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision,
if (precision > max_double_digits) precision = max_double_digits;
format_dragon(f, dragon_flags, precision, buf, exp);
}
if (!fixed && !specs.alt) {
if (!fixed && !specs.alt()) {
// Remove trailing zeros.
auto num_digits = buf.size();
while (num_digits > 0 && buf[num_digits - 1] == '0') {
@ -3462,12 +3462,12 @@ template <typename Char, typename OutputIt, typename T>
FMT_CONSTEXPR20 auto write_float(OutputIt out, T value, format_specs specs,
locale_ref loc) -> OutputIt {
// Use signbit because value < 0 is false for NaN.
sign_t sign = detail::signbit(value) ? sign::minus : specs.sign;
sign_t sign = detail::signbit(value) ? sign::minus : specs.sign();
if (!detail::isfinite(value))
return write_nonfinite<Char>(out, detail::isnan(value), specs, sign);
if (specs.align == align::numeric && sign) {
if (specs.align() == align::numeric && sign) {
*out++ = detail::sign<Char>(sign);
sign = sign::none;
if (specs.width != 0) --specs.width;
@ -3475,7 +3475,7 @@ FMT_CONSTEXPR20 auto write_float(OutputIt out, T value, format_specs specs,
int precision = specs.precision;
if (precision < 0) {
if (specs.type != presentation_type::none) {
if (specs.type() != presentation_type::none) {
precision = 6;
} else if (is_fast_float<T>::value && !is_constant_evaluated()) {
// Use Dragonbox for the shortest format.
@ -3486,21 +3486,21 @@ FMT_CONSTEXPR20 auto write_float(OutputIt out, T value, format_specs specs,
}
memory_buffer buffer;
if (specs.type == presentation_type::hexfloat) {
if (specs.type() == presentation_type::hexfloat) {
if (sign) buffer.push_back(detail::sign<char>(sign));
format_hexfloat(convert_float(value), specs, buffer);
return write_bytes<Char, align::right>(out, {buffer.data(), buffer.size()},
specs);
}
if (specs.type == presentation_type::exp) {
if (specs.type() == presentation_type::exp) {
if (precision == max_value<int>())
report_error("number is too big");
else
++precision;
specs.alt |= specs.precision != 0;
} else if (specs.type == presentation_type::fixed) {
specs.alt |= specs.precision != 0;
if (specs.precision != 0) specs.set_alt();
} else if (specs.type() == presentation_type::fixed) {
if (specs.precision != 0) specs.set_alt();
} else if (precision == 0) {
precision = 1;
}
@ -3517,7 +3517,7 @@ template <typename Char, typename OutputIt, typename T,
FMT_CONSTEXPR20 auto write(OutputIt out, T value, format_specs specs,
locale_ref loc = {}) -> OutputIt {
if (const_check(!is_supported_floating_point(value))) return out;
return specs.localized && write_loc(out, value, specs, loc)
return specs.localized() && write_loc(out, value, specs, loc)
? out
: write_float<Char>(out, value, specs, loc);
}
@ -3583,8 +3583,8 @@ template <typename Char, typename OutputIt, typename T,
FMT_ENABLE_IF(std::is_same<T, bool>::value)>
FMT_CONSTEXPR auto write(OutputIt out, T value, const format_specs& specs = {},
locale_ref = {}) -> OutputIt {
return specs.type != presentation_type::none &&
specs.type != presentation_type::string
return specs.type() != presentation_type::none &&
specs.type() != presentation_type::string
? write<Char>(out, value ? 1 : 0, specs, {})
: write_bytes<Char>(out, value ? "true" : "false", specs);
}
@ -4012,7 +4012,7 @@ template <typename T> struct formatter<group_digits_view<T>> : formatter<T> {
specs.width_ref, ctx);
detail::handle_dynamic_spec(specs.dynamic_precision(), specs.precision,
specs.precision_ref, ctx);
auto arg = detail::make_write_int_arg(t.value, specs.sign);
auto arg = detail::make_write_int_arg(t.value, specs.sign());
return detail::write_int(
ctx.out(), static_cast<detail::uint64_or_128_t<T>>(arg.abs_value),
arg.prefix, specs, detail::digit_grouping<char>("\3", ","));
@ -4039,13 +4039,12 @@ struct formatter<nested_view<T, Char>, Char> {
template <typename T, typename Char = char> struct nested_formatter {
private:
basic_specs specs_;
int width_;
detail::fill_t fill_;
align_t align_ : 4;
formatter<T, Char> formatter_;
public:
constexpr nested_formatter() : width_(0), align_(align_t::none) {}
constexpr nested_formatter() : width_(0) {}
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
-> decltype(ctx.begin()) {
@ -4053,8 +4052,7 @@ template <typename T, typename Char = char> struct nested_formatter {
if (it == end) return it;
auto specs = format_specs();
it = detail::parse_align(it, end, specs);
fill_ = specs.fill;
align_ = specs.align;
specs_ = specs;
Char c = *it;
auto width_ref = detail::arg_ref<Char>();
if ((c >= '0' && c <= '9') || c == '{') {
@ -4072,8 +4070,9 @@ template <typename T, typename Char = char> struct nested_formatter {
write(basic_appender<Char>(buf));
auto specs = format_specs();
specs.width = width_;
specs.fill = fill_;
specs.align = align_;
specs.set_fill(
basic_string_view<Char>(specs_.fill<Char>(), specs_.fill_size()));
specs.set_align(specs_.align());
return detail::write<Char>(
ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs);
}
@ -4159,7 +4158,7 @@ template <typename Char> struct format_handler {
auto specs = dynamic_format_specs<Char>();
begin = parse_format_specs(begin, end, specs, parse_context, arg.type());
if (specs.dynamic != 0) {
if (specs.dynamic()) {
handle_dynamic_spec(specs.dynamic_width(), specs.width, specs.width_ref,
context);
handle_dynamic_spec(specs.dynamic_precision(), specs.precision,
@ -4206,7 +4205,7 @@ template <typename T, typename Char, type TYPE>
template <typename FormatContext>
FMT_CONSTEXPR FMT_INLINE auto native_formatter<T, Char, TYPE>::format(
const T& val, FormatContext& ctx) const -> decltype(ctx.out()) {
if (specs_.dynamic == 0)
if (!specs_.dynamic())
return write<Char>(ctx.out(), val, specs_, ctx.locale());
auto specs = format_specs(specs_);
handle_dynamic_spec(specs.dynamic_width(), specs.width, specs_.width_ref,

View File

@ -200,7 +200,7 @@ class printf_width_handler {
auto operator()(T value) -> unsigned {
auto width = static_cast<uint32_or_64_or_128_t<T>>(value);
if (detail::is_negative(value)) {
specs_.align = align::left;
specs_.set_align(align::left);
width = 0 - width;
}
unsigned int_max = to_unsigned(max_value<int>());
@ -234,7 +234,7 @@ class printf_arg_formatter : public arg_formatter<Char> {
void write_null_pointer(bool is_string = false) {
auto s = this->specs;
s.type = presentation_type::none;
s.set_type(presentation_type::none);
write_bytes<Char>(this->out, is_string ? "(null)" : "(nil)", s);
}
@ -254,16 +254,17 @@ class printf_arg_formatter : public arg_formatter<Char> {
return;
}
format_specs s = this->specs;
if (s.type != presentation_type::none && s.type != presentation_type::chr) {
if (s.type() != presentation_type::none &&
s.type() != presentation_type::chr) {
return (*this)(static_cast<int>(value));
}
s.sign = sign::none;
s.alt = false;
s.fill = ' '; // Ignore '0' flag for char types.
s.set_sign(sign::none);
s.clear_alt();
s.set_fill(' '); // Ignore '0' flag for char types.
// align::numeric needs to be overwritten here since the '0' flag is
// ignored for non-numeric types
if (s.align == align::none || s.align == align::numeric)
s.align = align::right;
if (s.align() == align::none || s.align() == align::numeric)
s.set_align(align::right);
write<Char>(this->out, static_cast<Char>(value), s);
}
@ -276,14 +277,14 @@ class printf_arg_formatter : public arg_formatter<Char> {
if (value)
base::operator()(value);
else
write_null_pointer(this->specs.type != presentation_type::pointer);
write_null_pointer(this->specs.type() != presentation_type::pointer);
}
void operator()(const wchar_t* value) {
if (value)
base::operator()(value);
else
write_null_pointer(this->specs.type != presentation_type::pointer);
write_null_pointer(this->specs.type() != presentation_type::pointer);
}
void operator()(basic_string_view<Char> value) { base::operator()(value); }
@ -306,19 +307,19 @@ void parse_flags(format_specs& specs, const Char*& it, const Char* end) {
for (; it != end; ++it) {
switch (*it) {
case '-':
specs.align = align::left;
specs.set_align(align::left);
break;
case '+':
specs.sign = sign::plus;
specs.set_sign(sign::plus);
break;
case '0':
specs.fill = '0';
specs.set_fill('0');
break;
case ' ':
if (specs.sign != sign::plus) specs.sign = sign::space;
if (specs.sign() != sign::plus) specs.set_sign(sign::space);
break;
case '#':
specs.alt = true;
specs.set_alt();
break;
default:
return;
@ -339,7 +340,7 @@ auto parse_header(const Char*& it, const Char* end, format_specs& specs,
++it;
arg_index = value != -1 ? value : max_value<int>();
} else {
if (c == '0') specs.fill = '0';
if (c == '0') specs.set_fill('0');
if (value != 0) {
// Nonzero value means that we parsed width and don't need to
// parse it or flags again, so return now.
@ -444,7 +445,7 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
write(out, basic_string_view<Char>(start, to_unsigned(it - 1 - start)));
auto specs = format_specs();
specs.align = align::right;
specs.set_align(align::right);
// Parse argument index, flags and width.
int arg_index = parse_header(it, end, specs, get_arg);
@ -470,7 +471,7 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
// specified, the '0' flag is ignored
if (specs.precision >= 0 && arg.is_integral()) {
// Ignore '0' for non-numeric types or if '-' present.
specs.fill = ' ';
specs.set_fill(' ');
}
if (specs.precision >= 0 && arg.type() == type::cstring_type) {
auto str = arg.visit(get_cstring<Char>());
@ -480,13 +481,14 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
str, to_unsigned(nul != str_end ? nul - str : specs.precision));
arg = make_arg<basic_printf_context<Char>>(sv);
}
if (specs.alt && arg.visit(is_zero_int())) specs.alt = false;
if (specs.fill.template get<Char>() == '0') {
if (arg.is_arithmetic() && specs.align != align::left)
specs.align = align::numeric;
else
specs.fill = ' '; // Ignore '0' flag for non-numeric types or if '-'
// flag is also present.
if (specs.alt() && arg.visit(is_zero_int())) specs.clear_alt();
if (specs.fill_unit<Char>() == '0') {
if (arg.is_arithmetic() && specs.align() != align::left) {
specs.set_align(align::numeric);
} else {
// Ignore '0' flag for non-numeric types or if '-' flag is also present.
specs.set_fill(' ');
}
}
// Parse length and convert the argument to the required type.
@ -545,10 +547,10 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
}
}
bool upper = false;
specs.type = parse_printf_presentation_type(type, arg.type(), upper);
if (specs.type == presentation_type::none)
specs.set_type(parse_printf_presentation_type(type, arg.type(), upper));
if (specs.type() == presentation_type::none)
report_error("invalid format specifier");
specs.upper = upper;
if (upper) specs.set_upper();
start = it;

View File

@ -422,7 +422,7 @@ struct range_formatter<
auto buf = basic_memory_buffer<Char>();
for (; it != end; ++it) buf.push_back(*it);
auto specs = format_specs();
specs.type = presentation_type::debug;
specs.set_type(presentation_type::debug);
return detail::write<Char>(
out, basic_string_view<Char>(buf.data(), buf.size()), specs);
}

View File

@ -645,7 +645,7 @@ template <typename T, typename Char> struct formatter<std::complex<T>, Char> {
if (c.real() != 0) {
*out++ = Char('(');
out = detail::write<Char>(out, c.real(), specs, ctx.locale());
specs.sign = sign::plus;
specs.set_sign(sign::plus);
out = detail::write<Char>(out, c.imag(), specs, ctx.locale());
if (!detail::isfinite(c.imag())) *out++ = Char(' ');
*out++ = Char('i');
@ -670,7 +670,7 @@ template <typename T, typename Char> struct formatter<std::complex<T>, Char> {
auto format(const std::complex<T>& c, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto specs = specs_;
if (specs.dynamic != 0) {
if (specs.dynamic()) {
detail::handle_dynamic_spec(specs.dynamic_width(), specs.width,
specs.width_ref, ctx);
detail::handle_dynamic_spec(specs.dynamic_precision(), specs.precision,
@ -682,12 +682,14 @@ template <typename T, typename Char> struct formatter<std::complex<T>, Char> {
auto outer_specs = format_specs();
outer_specs.width = specs.width;
outer_specs.fill = specs.fill;
outer_specs.align = specs.align;
auto fill = specs.template fill<Char>();
if (fill)
outer_specs.set_fill(basic_string_view<Char>(fill, specs.fill_size()));
outer_specs.set_align(specs.align());
specs.width = 0;
specs.fill = {};
specs.align = align::none;
specs.set_fill({});
specs.set_align(align::none);
do_format(c, specs, ctx, basic_appender<Char>(buf));
return detail::write<Char>(ctx.out(),

View File

@ -516,19 +516,19 @@ template <size_t N> constexpr auto parse_test_specs(const char (&s)[N]) {
}
TEST(base_test, constexpr_parse_format_specs) {
static_assert(parse_test_specs("<").align == fmt::align::left, "");
static_assert(parse_test_specs("*^").fill.get<char>() == '*', "");
static_assert(parse_test_specs("+").sign == fmt::sign::plus, "");
static_assert(parse_test_specs("-").sign == fmt::sign::none, "");
static_assert(parse_test_specs(" ").sign == fmt::sign::space, "");
static_assert(parse_test_specs("#").alt, "");
static_assert(parse_test_specs("0").align == fmt::align::numeric, "");
static_assert(parse_test_specs("L").localized, "");
static_assert(parse_test_specs("<").align() == fmt::align::left, "");
static_assert(parse_test_specs("*^").fill_unit<char>() == '*', "");
static_assert(parse_test_specs("+").sign() == fmt::sign::plus, "");
static_assert(parse_test_specs("-").sign() == fmt::sign::none, "");
static_assert(parse_test_specs(" ").sign() == fmt::sign::space, "");
static_assert(parse_test_specs("#").alt(), "");
static_assert(parse_test_specs("0").align() == fmt::align::numeric, "");
static_assert(parse_test_specs("L").localized(), "");
static_assert(parse_test_specs("42").width == 42, "");
static_assert(parse_test_specs("{42}").width_ref.index == 42, "");
static_assert(parse_test_specs(".42").precision == 42, "");
static_assert(parse_test_specs(".{42}").precision_ref.index == 42, "");
static_assert(parse_test_specs("f").type == fmt::presentation_type::fixed,
static_assert(parse_test_specs("f").type() == fmt::presentation_type::fixed,
"");
}

View File

@ -368,7 +368,7 @@ const char* parse_scan_specs(const char* begin, const char* end,
switch (to_ascii(*begin)) {
// TODO: parse more scan format specifiers
case 'x':
specs.type = presentation_type::hex;
specs.set_type(presentation_type::hex);
++begin;
break;
case '}':
@ -437,7 +437,7 @@ auto read_hex(scan_iterator it, T& value) -> scan_iterator {
template <typename T, FMT_ENABLE_IF(std::is_unsigned<T>::value)>
auto read(scan_iterator it, T& value, const format_specs& specs)
-> scan_iterator {
if (specs.type == presentation_type::hex) return read_hex(it, value);
if (specs.type() == presentation_type::hex) return read_hex(it, value);
return read(it, value);
}