Skip to content

Commit

Permalink
Reduce format specs size
Browse files Browse the repository at this point in the history
  • Loading branch information
vitaut committed Aug 10, 2024
1 parent 9831431 commit 496bf6d
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 85 deletions.
101 changes: 61 additions & 40 deletions include/fmt/base.h
Original file line number Diff line number Diff line change
Expand Up @@ -2136,6 +2136,9 @@ struct fill_t {
return nullptr;
}
};

enum class arg_id_kind { none, index, name };

} // namespace detail

enum class presentation_type : unsigned char {
Expand Down Expand Up @@ -2168,6 +2171,7 @@ struct format_specs {
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;
Expand All @@ -2179,38 +2183,31 @@ struct format_specs {
type(presentation_type::none),
align(align::none),
sign(sign::none),
dynamic(0),
upper(false),
alt(false),
localized(false) {}

enum { dynamic_width_mask = 3, dynamic_precision_mask = 12 };

constexpr auto dynamic_width() const -> detail::arg_id_kind {
return static_cast<detail::arg_id_kind>(dynamic & dynamic_width_mask);
}
constexpr auto dynamic_precision() const -> detail::arg_id_kind {
return static_cast<detail::arg_id_kind>(
(dynamic & dynamic_precision_mask) >> 2);
}
};

namespace detail {

enum class arg_id_kind { none, index, name };

// An argument reference.
template <typename Char> struct arg_ref {
FMT_CONSTEXPR arg_ref() : kind(arg_id_kind::none), val() {}

FMT_CONSTEXPR explicit arg_ref(int index)
: kind(arg_id_kind::index), val(index) {}
FMT_CONSTEXPR explicit arg_ref(basic_string_view<Char> name)
: kind(arg_id_kind::name), val(name) {}

FMT_CONSTEXPR auto operator=(int idx) -> arg_ref& {
kind = arg_id_kind::index;
val.index = idx;
return *this;
}
template <typename Char> union arg_ref {
FMT_CONSTEXPR arg_ref(int idx = 0) : index(idx) {}
FMT_CONSTEXPR arg_ref(basic_string_view<Char> n) : name(n) {}

arg_id_kind kind;
union value {
FMT_CONSTEXPR value(int idx = 0) : index(idx) {}
FMT_CONSTEXPR value(basic_string_view<Char> n) : name(n) {}

int index;
basic_string_view<Char> name;
} val;
int index;
basic_string_view<Char> name;
};

// Format specifiers with width and precision resolved at formatting rather
Expand Down Expand Up @@ -2321,28 +2318,37 @@ FMT_CONSTEXPR auto parse_arg_id(const Char* begin, const Char* end,
return it;
}

template <typename Char> struct dynamic_spec_id_handler {
template <typename Char> struct dynamic_spec_handler {
basic_format_parse_context<Char>& ctx;
arg_ref<Char>& ref;
arg_id_kind& kind;

FMT_CONSTEXPR void on_index(int id) {
ref = arg_ref<Char>(id);
ref = id;
kind = arg_id_kind::index;
ctx.check_arg_id(id);
ctx.check_dynamic_spec(id);
}
FMT_CONSTEXPR void on_name(basic_string_view<Char> id) {
ref = arg_ref<Char>(id);
ref = id;
kind = arg_id_kind::name;
ctx.check_arg_id(id);
}
};

template <typename Char> struct parse_dynamic_spec_result {
const Char* end;
arg_id_kind kind;
};

// Parses integer | "{" [arg_id] "}".
template <typename Char>
FMT_CONSTEXPR auto parse_dynamic_spec(const Char* begin, const Char* end,
int& value, arg_ref<Char>& ref,
basic_format_parse_context<Char>& ctx)
-> const Char* {
-> parse_dynamic_spec_result<Char> {
FMT_ASSERT(begin != end, "");
auto kind = arg_id_kind::none;
if ('0' <= *begin && *begin <= '9') {
int val = parse_nonnegative_int(begin, end, -1);
if (val == -1) report_error("number is too big");
Expand All @@ -2354,31 +2360,47 @@ FMT_CONSTEXPR auto parse_dynamic_spec(const Char* begin, const Char* end,
Char c = *begin;
if (c == '}' || c == ':') {
int id = ctx.next_arg_id();
ref = arg_ref<Char>(id);
ref = id;
kind = arg_id_kind::index;
ctx.check_dynamic_spec(id);
} else {
begin =
parse_arg_id(begin, end, dynamic_spec_id_handler<Char>{ctx, ref});
begin = parse_arg_id(begin, end,
dynamic_spec_handler<Char>{ctx, ref, kind});
}
}
if (begin != end && *begin == '}') return ++begin;
if (begin != end && *begin == '}') return {++begin, kind};
}
report_error("invalid format string");
}
return begin;
return {begin, kind};
}

template <typename Char>
FMT_CONSTEXPR auto parse_width(const Char* begin, const Char* end,
format_specs& specs, arg_ref<Char>& width_ref,
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;
return result.end;
}

template <typename Char>
FMT_CONSTEXPR auto parse_precision(const Char* begin, const Char* end,
int& value, arg_ref<Char>& ref,
format_specs& specs,
arg_ref<Char>& precision_ref,
basic_format_parse_context<Char>& ctx)
-> const Char* {
++begin;
if (begin != end)
begin = parse_dynamic_spec(begin, end, value, ref, ctx);
else
if (begin == end) {
report_error("invalid precision");
return begin;
return begin;
}
auto result =
parse_dynamic_spec(begin, end, specs.precision, precision_ref, ctx);
specs.dynamic =
(specs.dynamic | (static_cast<unsigned char>(result.kind) << 2)) & 0xfu;
return result.end;
}

enum class state { start, align, sign, hash, zero, width, precision, locale };
Expand Down Expand Up @@ -2466,13 +2488,12 @@ FMT_CONSTEXPR auto parse_format_specs(const Char* begin, const Char* end,
case '9':
case '{':
enter_state(state::width);
begin = parse_dynamic_spec(begin, end, specs.width, specs.width_ref, ctx);
begin = parse_width(begin, end, specs, specs.width_ref, ctx);
break;
case '.':
enter_state(state::precision,
in(arg_type, float_set | string_set | cstring_set));
begin = parse_precision(begin, end, specs.precision, specs.precision_ref,
ctx);
begin = parse_precision(begin, end, specs, specs.precision_ref, ctx);
break;
case 'L':
enter_state(state::locale, is_arithmetic_type(arg_type));
Expand Down
16 changes: 9 additions & 7 deletions include/fmt/chrono.h
Original file line number Diff line number Diff line change
Expand Up @@ -2253,15 +2253,14 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {

Char c = *it;
if ((c >= '0' && c <= '9') || c == '{') {
it = detail::parse_dynamic_spec(it, end, specs_.width, width_ref_, ctx);
it = detail::parse_width(it, end, specs_, width_ref_, ctx);
if (it == end) return it;
}

auto checker = detail::chrono_format_checker();
if (*it == '.') {
checker.has_precision_integral = !std::is_floating_point<Rep>::value;
it = detail::parse_precision(it, end, specs_.precision, precision_ref_,
ctx);
it = detail::parse_precision(it, end, specs_, precision_ref_, ctx);
}
if (it != end && *it == 'L') {
localized_ = true;
Expand All @@ -2283,8 +2282,10 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
// is not specified.
auto buf = basic_memory_buffer<Char>();
auto out = basic_appender<Char>(buf);
detail::handle_dynamic_spec(specs.width, width_ref_, ctx);
detail::handle_dynamic_spec(precision, precision_ref_, ctx);
detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_,
ctx);
detail::handle_dynamic_spec(specs.dynamic_precision(), precision,
precision_ref_, ctx);
if (begin == end || *begin == '}') {
out = detail::format_duration_value<Char>(out, d.count(), precision);
detail::format_duration_unit<Char, Period>(out);
Expand Down Expand Up @@ -2390,7 +2391,8 @@ template <typename Char> struct formatter<std::tm, Char> {
auto specs = specs_;
auto buf = basic_memory_buffer<Char>();
auto out = basic_appender<Char>(buf);
detail::handle_dynamic_spec(specs.width, width_ref_, ctx);
detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_,
ctx);

auto loc_ref = ctx.locale();
detail::get_locale loc(static_cast<bool>(loc_ref), loc_ref);
Expand All @@ -2412,7 +2414,7 @@ template <typename Char> struct formatter<std::tm, Char> {

Char c = *it;
if ((c >= '0' && c <= '9') || c == '{') {
it = detail::parse_dynamic_spec(it, end, specs_.width, width_ref_, ctx);
it = detail::parse_width(it, end, specs_, width_ref_, ctx);
if (it == end) return it;
}

Expand Down
19 changes: 11 additions & 8 deletions include/fmt/compile.h
Original file line number Diff line number Diff line change
Expand Up @@ -269,32 +269,36 @@ constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
}

template <typename Char> struct arg_id_handler {
arg_id_kind kind;
arg_ref<Char> arg_id;

constexpr int on_auto() {
FMT_ASSERT(false, "handler cannot be used with automatic indexing");
return 0;
}
constexpr int on_index(int id) {
kind = arg_id_kind::index;
arg_id = arg_ref<Char>(id);
return 0;
}
constexpr int on_name(basic_string_view<Char> id) {
kind = arg_id_kind::name;
arg_id = arg_ref<Char>(id);
return 0;
}
};

template <typename Char> struct parse_arg_id_result {
arg_id_kind kind;
arg_ref<Char> arg_id;
const Char* arg_id_end;
};

template <int ID, typename Char>
constexpr auto parse_arg_id(const Char* begin, const Char* end) {
auto handler = arg_id_handler<Char>{arg_ref<Char>{}};
auto handler = arg_id_handler<Char>{arg_id_kind::none, arg_ref<Char>{}};
auto arg_id_end = parse_arg_id(begin, end, handler);
return parse_arg_id_result<Char>{handler.arg_id, arg_id_end};
return parse_arg_id_result<Char>{handler.kind, handler.arg_id, arg_id_end};
}

template <typename T, typename Enable = void> struct field_type {
Expand Down Expand Up @@ -357,18 +361,18 @@ constexpr auto compile_format_string(S fmt) {
constexpr char_type c =
arg_id_end_pos != str.size() ? str[arg_id_end_pos] : char_type();
static_assert(c == '}' || c == ':', "missing '}' in format string");
if constexpr (arg_id_result.arg_id.kind == arg_id_kind::index) {
if constexpr (arg_id_result.kind == arg_id_kind::index) {
static_assert(
ID == manual_indexing_id || ID == 0,
"cannot switch from automatic to manual argument indexing");
constexpr auto arg_index = arg_id_result.arg_id.val.index;
constexpr auto arg_index = arg_id_result.arg_id.index;
return parse_replacement_field_then_tail<get_type<arg_index, Args>,
Args, arg_id_end_pos,
arg_index, manual_indexing_id>(
fmt);
} else if constexpr (arg_id_result.arg_id.kind == arg_id_kind::name) {
} else if constexpr (arg_id_result.kind == arg_id_kind::name) {
constexpr auto arg_index =
get_arg_index_by_name(arg_id_result.arg_id.val.name, Args{});
get_arg_index_by_name(arg_id_result.arg_id.name, Args{});
if constexpr (arg_index >= 0) {
constexpr auto next_id =
ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
Expand All @@ -377,8 +381,7 @@ constexpr auto compile_format_string(S fmt) {
arg_index, next_id>(fmt);
} else if constexpr (c == '}') {
return parse_tail<Args, arg_id_end_pos + 1, ID>(
runtime_named_field<char_type>{arg_id_result.arg_id.val.name},
fmt);
runtime_named_field<char_type>{arg_id_result.arg_id.name}, fmt);
} else if constexpr (c == ':') {
return unknown_format(); // no type info for specs parsing
}
Expand Down
Loading

0 comments on commit 496bf6d

Please sign in to comment.