Add FMT_USE_SMALLEST_INT flag

When defined and set to zero, will use the largest available integer
container for writing ints. The has the benefit of reducing instances
the of int_writer class which will reduce the binary cost.
This commit is contained in:
Khalil Estell 2020-07-18 15:03:27 -07:00
parent b8f53a0813
commit cb0a609280
2 changed files with 17 additions and 44 deletions

View File

@ -311,6 +311,10 @@ struct int128_t {};
struct uint128_t {}; struct uint128_t {};
#endif #endif
#if !defined(FMT_USE_SMALLEST_INT)
#define FMT_USE_SMALLEST_INT 1
#endif
// Casts a nonnegative integer to unsigned. // Casts a nonnegative integer to unsigned.
template <typename Int> template <typename Int>
FMT_CONSTEXPR typename std::make_unsigned<Int>::type to_unsigned(Int value) { FMT_CONSTEXPR typename std::make_unsigned<Int>::type to_unsigned(Int value) {

View File

@ -726,18 +726,19 @@ FMT_CONSTEXPR bool is_supported_floating_point(T) {
(std::is_same<T, long double>::value && FMT_USE_LONG_DOUBLE); (std::is_same<T, long double>::value && FMT_USE_LONG_DOUBLE);
} }
#if FMT_USE_SMALLEST_INT
// Smallest of uint32_t, uint64_t, uint128_t that is large enough to // Smallest of uint32_t, uint64_t, uint128_t that is large enough to
// represent all values of T. // represent all values of T.
template <typename T> template <typename T>
using uint32_or_64_or_128_t = conditional_t< using uint32_or_64_or_128_t = conditional_t<
std::numeric_limits<T>::digits <= 32, uint32_t, std::numeric_limits<T>::digits <= 32, uint32_t,
conditional_t<std::numeric_limits<T>::digits <= 64, uint64_t, uint128_t>>; conditional_t<std::numeric_limits<T>::digits <= 64, uint64_t, uint128_t>>;
#else
// Selects the between uint64_t or uint128_t based on the how uint128_t is // Pick the largest integer container to represent represent all values of T.
// defined. If macro FMT_USE_INT128 defined as 0, then its size will be 1 byte, template <typename T>
// meaning the largest sized int that can be used is uint64_t. using uint32_or_64_or_128_t =
using uint_largest_t =
conditional_t<sizeof(uint128_t) < sizeof(uint64_t), uint64_t, uint128_t>; conditional_t<sizeof(uint128_t) < sizeof(uint64_t), uint64_t, uint128_t>;
#endif
// Static data is placed in this class template for the header-only config. // Static data is placed in this class template for the header-only config.
template <typename T = void> struct FMT_EXTERN_TEMPLATE_API basic_data { template <typename T = void> struct FMT_EXTERN_TEMPLATE_API basic_data {
@ -1464,21 +1465,11 @@ OutputIt write(OutputIt out, basic_string_view<StrChar> s,
} }
// The handle_int_type_spec handler that writes an integer. // The handle_int_type_spec handler that writes an integer.
template <typename OutputIt, typename Char> struct int_writer { template <typename OutputIt, typename Char, typename UInt> struct int_writer {
enum class int_bytes {
byte1 = sizeof(uint8_t),
byte2 = sizeof(uint16_t),
byte4 = sizeof(uint32_t),
byte8 = sizeof(uint64_t),
byte16 = 16, // Must be directly set because uint128_t can be 16 bytes if
// FMT_USE_INT128 == 1 and 1 byte if FMT_USE_INT128 == 0.
};
OutputIt out; OutputIt out;
locale_ref locale; locale_ref locale;
const basic_format_specs<Char>& specs; const basic_format_specs<Char>& specs;
uint_largest_t abs_value; UInt abs_value;
int_bytes value_bytes;
char prefix[4]; char prefix[4];
unsigned prefix_size; unsigned prefix_size;
@ -1493,11 +1484,9 @@ template <typename OutputIt, typename Char> struct int_writer {
: out(output), : out(output),
locale(loc), locale(loc),
specs(s), specs(s),
abs_value(static_cast<decltype(abs_value)>(value)), abs_value(static_cast<UInt>(value)),
value_bytes(int_bytes::byte8),
prefix_size(0) { prefix_size(0) {
value_bytes = static_cast<int_bytes>(sizeof(value)); static_assert(std::is_same<uint32_or_64_or_128_t<Int>, UInt>::value, "");
if (is_negative(value)) { if (is_negative(value)) {
prefix[0] = '-'; prefix[0] = '-';
++prefix_size; ++prefix_size;
@ -1509,28 +1498,7 @@ template <typename OutputIt, typename Char> struct int_writer {
} }
void on_dec() { void on_dec() {
int num_digits = 0; auto num_digits = count_digits(abs_value);
switch (value_bytes) {
case int_bytes::byte1:
case int_bytes::byte2:
case int_bytes::byte4:
num_digits = count_digits(static_cast<uint32_t>(abs_value));
break;
case int_bytes::byte8:
num_digits = count_digits(static_cast<uint64_t>(abs_value));
break;
#if !FMT_USE_INT128
case int_bytes::byte16:
num_digits = count_digits(static_cast<uint64_t>(abs_value));
break;
#else
case int_bytes::byte16:
num_digits = count_digits(abs_value);
break;
#endif
}
out = write_int( out = write_int(
out, num_digits, get_prefix(), specs, [this, num_digits](iterator it) { out, num_digits, get_prefix(), specs, [this, num_digits](iterator it) {
return format_decimal<Char>(it, abs_value, num_digits).end; return format_decimal<Char>(it, abs_value, num_digits).end;
@ -1870,7 +1838,8 @@ class arg_formatter_base {
detail::reserve(std::declval<iterator&>(), 0))>; detail::reserve(std::declval<iterator&>(), 0))>;
template <typename T> void write_int(T value, const format_specs& spec) { template <typename T> void write_int(T value, const format_specs& spec) {
int_writer<iterator, Char> w(out_, locale_, value, 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); handle_int_type_spec(spec.type, w);
out_ = w.out; out_ = w.out;
} }