Optimize %F in tm formatting
This commit is contained in:
parent
1aa98f8b93
commit
67cb2dad37
@ -482,6 +482,32 @@ inline size_t strftime(wchar_t* str, size_t count, const wchar_t* format,
|
|||||||
return wcsftime(str, count, format, time);
|
return wcsftime(str, count, format, time);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Writes two-digit numbers a, b and c separated by sep to buf.
|
||||||
|
// The method by Pavel Novikov based on
|
||||||
|
// https://johnnylee-sde.github.io/Fast-unsigned-integer-to-time-string/.
|
||||||
|
void write_digit2_separated(char* buf, unsigned a, unsigned b, unsigned c,
|
||||||
|
char sep) {
|
||||||
|
unsigned long long digits =
|
||||||
|
a | (b << 24) | (static_cast<unsigned long long>(c) << 48);
|
||||||
|
// Convert each value to BCD.
|
||||||
|
// We have x = a * 10 + b and we want to convert it to BCD y = a * 16 + b.
|
||||||
|
// The difference is
|
||||||
|
// y - x = a * 6
|
||||||
|
// a can be found from x:
|
||||||
|
// a = floor(x / 10)
|
||||||
|
// then
|
||||||
|
// y = x + a * 6 = x + floor(x / 10) * 6
|
||||||
|
// floor(x / 10) is (x * 205) >> 11 (needs 16 bits).
|
||||||
|
digits += (((digits * 205) >> 11) & 0x000f00000f00000f) * 6;
|
||||||
|
// Put low nibbles to high bytes and high nibbles to low bytes.
|
||||||
|
digits = ((digits & 0x00f00000f00000f0) >> 4) |
|
||||||
|
((digits & 0x000f00000f00000f) << 8);
|
||||||
|
auto usep = static_cast<unsigned long long>(sep);
|
||||||
|
// Add ASCII '0' to each digit byte and insert separators.
|
||||||
|
digits |= 0x3030003030003030 | (usep << 16) | (usep << 40);
|
||||||
|
memcpy(buf, &digits, 8);
|
||||||
|
}
|
||||||
|
|
||||||
FMT_END_DETAIL_NAMESPACE
|
FMT_END_DETAIL_NAMESPACE
|
||||||
|
|
||||||
template <typename Char, typename Duration>
|
template <typename Char, typename Duration>
|
||||||
@ -519,19 +545,40 @@ constexpr Char
|
|||||||
Char>::default_specs[];
|
Char>::default_specs[];
|
||||||
|
|
||||||
template <typename Char> struct formatter<std::tm, Char> {
|
template <typename Char> struct formatter<std::tm, Char> {
|
||||||
|
private:
|
||||||
|
enum class spec {
|
||||||
|
unknown,
|
||||||
|
year_month_day,
|
||||||
|
};
|
||||||
|
spec spec_ = spec::unknown;
|
||||||
|
|
||||||
|
public:
|
||||||
|
basic_string_view<Char> specs;
|
||||||
|
|
||||||
template <typename ParseContext>
|
template <typename ParseContext>
|
||||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||||
auto it = ctx.begin();
|
auto it = ctx.begin();
|
||||||
if (it != ctx.end() && *it == ':') ++it;
|
if (it != ctx.end() && *it == ':') ++it;
|
||||||
auto end = it;
|
auto end = it;
|
||||||
while (end != ctx.end() && *end != '}') ++end;
|
while (end != ctx.end() && *end != '}') ++end;
|
||||||
specs = {it, detail::to_unsigned(end - it)};
|
auto size = detail::to_unsigned(end - it);
|
||||||
|
specs = {it, size};
|
||||||
|
if (specs == string_view("%F", 2)) spec_ = spec::year_month_day;
|
||||||
return end;
|
return end;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename FormatContext>
|
template <typename FormatContext>
|
||||||
auto format(const std::tm& tm, FormatContext& ctx) const
|
auto format(const std::tm& tm, FormatContext& ctx) const
|
||||||
-> decltype(ctx.out()) {
|
-> decltype(ctx.out()) {
|
||||||
|
auto year = 1900 + tm.tm_year;
|
||||||
|
if (spec_ == spec::year_month_day && year >= 0 && year < 10000) {
|
||||||
|
char buf[10];
|
||||||
|
detail::copy2(buf, detail::data::digits[year / 100]);
|
||||||
|
detail::write_digit2_separated(buf + 2, year % 100,
|
||||||
|
detail::to_unsigned(tm.tm_mon + 1),
|
||||||
|
detail::to_unsigned(tm.tm_mday), '-');
|
||||||
|
return std::copy_n(buf, sizeof(buf), ctx.out());
|
||||||
|
}
|
||||||
basic_memory_buffer<Char> tm_format;
|
basic_memory_buffer<Char> tm_format;
|
||||||
tm_format.append(specs.begin(), specs.end());
|
tm_format.append(specs.begin(), specs.end());
|
||||||
// By appending an extra space we can distinguish an empty result that
|
// By appending an extra space we can distinguish an empty result that
|
||||||
@ -554,8 +601,6 @@ template <typename Char> struct formatter<std::tm, Char> {
|
|||||||
// Remove the extra space.
|
// Remove the extra space.
|
||||||
return std::copy(buf.begin(), buf.end() - 1, ctx.out());
|
return std::copy(buf.begin(), buf.end() - 1, ctx.out());
|
||||||
}
|
}
|
||||||
|
|
||||||
basic_string_view<Char> specs;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
FMT_BEGIN_DETAIL_NAMESPACE
|
FMT_BEGIN_DETAIL_NAMESPACE
|
||||||
@ -1170,6 +1215,8 @@ class weekday {
|
|||||||
: value(static_cast<unsigned char>(wd != 7 ? wd : 0)) {}
|
: value(static_cast<unsigned char>(wd != 7 ? wd : 0)) {}
|
||||||
constexpr unsigned c_encoding() const noexcept { return value; }
|
constexpr unsigned c_encoding() const noexcept { return value; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class year_month_day {};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// A rudimentary weekday formatter.
|
// A rudimentary weekday formatter.
|
||||||
|
@ -48,6 +48,7 @@ TEST(chrono_test, format_tm) {
|
|||||||
tm.tm_sec = 33;
|
tm.tm_sec = 33;
|
||||||
EXPECT_EQ(fmt::format("The date is {:%Y-%m-%d %H:%M:%S}.", tm),
|
EXPECT_EQ(fmt::format("The date is {:%Y-%m-%d %H:%M:%S}.", tm),
|
||||||
"The date is 2016-04-25 11:22:33.");
|
"The date is 2016-04-25 11:22:33.");
|
||||||
|
EXPECT_EQ(fmt::format("{:%F}", tm), "2016-04-25");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(chrono_test, grow_buffer) {
|
TEST(chrono_test, grow_buffer) {
|
||||||
|
Loading…
Reference in New Issue
Block a user