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 {};
#endif
#if !defined(FMT_USE_SMALLEST_INT)
#define FMT_USE_SMALLEST_INT 1
#endif
// Casts a nonnegative integer to unsigned.
template <typename Int>
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);
}
#if FMT_USE_SMALLEST_INT
// Smallest of uint32_t, uint64_t, uint128_t that is large enough to
// represent all values of T.
template <typename T>
using uint32_or_64_or_128_t = conditional_t<
std::numeric_limits<T>::digits <= 32, uint32_t,
conditional_t<std::numeric_limits<T>::digits <= 64, uint64_t, uint128_t>>;
// Selects the between uint64_t or uint128_t based on the how uint128_t is
// defined. If macro FMT_USE_INT128 defined as 0, then its size will be 1 byte,
// meaning the largest sized int that can be used is uint64_t.
using uint_largest_t =
#else
// Pick the largest integer container to represent represent all values of T.
template <typename T>
using uint32_or_64_or_128_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.
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.
template <typename OutputIt, typename Char> 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.
};
template <typename OutputIt, typename Char, typename UInt> struct int_writer {
OutputIt out;
locale_ref locale;
const basic_format_specs<Char>& specs;
uint_largest_t abs_value;
int_bytes value_bytes;
UInt abs_value;
char prefix[4];
unsigned prefix_size;
@ -1493,11 +1484,9 @@ template <typename OutputIt, typename Char> struct int_writer {
: out(output),
locale(loc),
specs(s),
abs_value(static_cast<decltype(abs_value)>(value)),
value_bytes(int_bytes::byte8),
abs_value(static_cast<UInt>(value)),
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)) {
prefix[0] = '-';
++prefix_size;
@ -1509,28 +1498,7 @@ template <typename OutputIt, typename Char> struct int_writer {
}
void on_dec() {
int num_digits = 0;
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
}
auto num_digits = count_digits(abs_value);
out = write_int(
out, num_digits, get_prefix(), specs, [this, num_digits](iterator it) {
return format_decimal<Char>(it, abs_value, num_digits).end;
@ -1870,7 +1838,8 @@ class arg_formatter_base {
detail::reserve(std::declval<iterator&>(), 0))>;
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);
out_ = w.out;
}