#pragma once #include #include class pow_format { public: pow_format(long long val, std::string&& unit, bool binary = false, bool skip_decimal = false, int min_pow_for_decimal = 0) : val_(val), unit_(unit), binary_(binary), skip_decimal_(skip_decimal), min_pow_for_decimal_(min_pow_for_decimal) {}; long long val_; std::string unit_; bool binary_; bool skip_decimal_; int min_pow_for_decimal_; }; namespace fmt { template <> struct formatter { char spec = 0; int width = 0; template constexpr auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { auto it = ctx.begin(), end = ctx.end(); if (it != end && *it == ':') ++it; if (it && (*it == '>' || *it == '<' || *it == '=')) { spec = *it; ++it; } if (it == end || *it == '}') return it; if ('0' <= *it && *it <= '9') { // We ignore it for now, but keep it for compatibility with // existing configs where the format for pow_format'ed numbers was // 'string' and specifications such as {:>9} were valid. // The rationale for ignoring it is that the only reason to specify // an alignment and a with is to get a fixed width bar, and ">" is // sufficient in this implementation. #if FMT_VERSION < 80000 width = parse_nonnegative_int(it, end, ctx); #else width = detail::parse_nonnegative_int(it, end, -1); #endif } return it; } template auto format(const pow_format& s, FormatContext& ctx) const -> decltype(ctx.out()) { const char* units[] = {"", "k", "M", "G", "T", "P", nullptr}; auto base = s.binary_ ? 1024ull : 1000ll; auto div = 1ll; auto fraction = (double)s.val_; int pow; for (pow = 0; units[pow + 1] != nullptr && fraction / base >= 1; ++pow) { fraction /= base; div *= base; } auto fixed_precision = (s.skip_decimal_ && ((s.val_ % div) == 0)) ? 0 : 1; auto number_width = 3 + fixed_precision // coeff in {:.{fixed_precision}f} format + (fixed_precision != 0) // float dot + s.binary_; // potential digit before the decimal point auto max_width = number_width + 1 // prefix from units array + s.binary_ // for the 'i' in GiB. + s.unit_.length(); const char* format; std::string string; switch (spec) { case '>': return fmt::format_to(ctx.out(), "{:>{}}", fmt::format("{}", s), max_width); case '<': return fmt::format_to(ctx.out(), "{:<{}}", fmt::format("{}", s), max_width); case '=': format = "{coefficient:<{number_width}.{fixed_precision}f}{padding}{prefix}{unit}"; break; case 0: default: format = pow < s.min_pow_for_decimal_ ? "{coefficient:.0f}{prefix}{unit}" : "{coefficient:.{fixed_precision}f}{prefix}{unit}"; break; } return fmt::format_to( ctx.out(), fmt::runtime(format), fmt::arg("coefficient", fraction), fmt::arg("fixed_precision", fixed_precision), fmt::arg("number_width", number_width), fmt::arg("prefix", std::string() + units[pow] + ((s.binary_ && pow) ? "i" : "")), fmt::arg("unit", s.unit_), fmt::arg("padding", pow ? "" : s.binary_ ? " " : " ")); } }; // Glib ustirng support template <> struct formatter : formatter { template auto format(const Glib::ustring& value, FormatContext& ctx) const { return formatter::format(static_cast(value), ctx); } }; } // namespace fmt