Fix outputting extreme integer values in edge cases

For some gcc version (Ubuntu 5.5.0-12ubuntu1~16.04) the existing code
crashes when the minimum value of int64_t is outputted.

Resurrect the code from before 546e2cbf (🚨 fixed some warnings,
2019-03-13) but delegate the sign removal so that the compilers don't
complain about taking the negative value of an unsigned value.

In addition we also rewrite the expression so that we first increment
and then negate.

The definition of remove_sign(number_unsigned_t) is never called as
unsigned values are never negative.
This commit is contained in:
Thomas Braun 2019-08-23 23:52:48 +02:00
parent bf4156056b
commit 6ce2f35ba8
2 changed files with 54 additions and 2 deletions

View file

@ -630,7 +630,7 @@ class serializer
if (is_negative)
{
*buffer_ptr = '-';
abs_value = static_cast<number_unsigned_t>(std::abs(static_cast<std::intmax_t>(x)));
abs_value = remove_sign(x);
// account one more byte for the minus sign
n_chars = 1 + count_digits(abs_value);
@ -811,6 +811,32 @@ class serializer
return state;
}
/*
* Overload to make the compiler happy while it is instantiating
* dump_integer for number_unsigned_t.
* Must never be called.
*/
number_unsigned_t remove_sign(number_unsigned_t x)
{
assert(false); // LCOV_EXCL_LINE
return x; // LCOV_EXCL_LINE
}
/*
* Helper function for dump_integer
*
* This function takes a negative signed integer and returns its absolute
* value as unsigned integer. The plus/minus shuffling is necessary as we can
* not directly remove the sign of an arbitrary signed integer as the
* absolute values of INT_MIN and INT_MAX are usually not the same. See
* #1708 for details.
*/
inline number_unsigned_t remove_sign(number_integer_t x) noexcept
{
assert(x < 0 and x < (std::numeric_limits<number_integer_t>::max)());
return static_cast<number_unsigned_t>(-(x + 1)) + 1;
}
private:
/// the output of the serializer
output_adapter_t<char> o = nullptr;

View file

@ -14264,7 +14264,7 @@ class serializer
if (is_negative)
{
*buffer_ptr = '-';
abs_value = static_cast<number_unsigned_t>(std::abs(static_cast<std::intmax_t>(x)));
abs_value = remove_sign(x);
// account one more byte for the minus sign
n_chars = 1 + count_digits(abs_value);
@ -14445,6 +14445,32 @@ class serializer
return state;
}
/*
* Overload to make the compiler happy while it is instantiating
* dump_integer for number_unsigned_t.
* Must never be called.
*/
number_unsigned_t remove_sign(number_unsigned_t x)
{
assert(false); // LCOV_EXCL_LINE
return x; // LCOV_EXCL_LINE
}
/*
* Helper function for dump_integer
*
* This function takes a negative signed integer and returns its absolute
* value as unsigned integer. The plus/minus shuffling is necessary as we can
* not directly remove the sign of an arbitrary signed integer as the
* absolute values of INT_MIN and INT_MAX are usually not the same. See
* #1708 for details.
*/
inline number_unsigned_t remove_sign(number_integer_t x) noexcept
{
assert(x < 0 and x < (std::numeric_limits<number_integer_t>::max)());
return static_cast<number_unsigned_t>(-(x + 1)) + 1;
}
private:
/// the output of the serializer
output_adapter_t<char> o = nullptr;