Skip to content

Commit

Permalink
changed output of formatNumberReadable so that it shows powers of two…
Browse files Browse the repository at this point in the history
… and one-less-than powers of two in a more compact format
  • Loading branch information
Ruko97 committed Sep 7, 2022
1 parent 548a4b4 commit 9feab34
Show file tree
Hide file tree
Showing 10 changed files with 116 additions and 98 deletions.
124 changes: 63 additions & 61 deletions libsolutil/StringUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,109 +101,111 @@ std::string joinHumanReadablePrefixed
return _separator + joinHumanReadable(_list, _separator, _lastSeparator);
}

/// Same as @ref formatNumberReadable but only for unsigned numbers
/// Formats large numbers to be easily readable by humans.
/// Returns decimal representation for smaller numbers; hex for large numbers.
/// "Special" numbers, powers-of-two and powers-of-two minus 1, are returned in
/// formulaic form like 0x01 * 2**24 - 1.
/// @a T can be any integer value, will typically be u160, u256 or bigint.
/// @param _value to be formatted
/// @param _useTruncation if true, internal truncation is also applied,
/// like 0x5555...{+56 more}...5555
/// @example formatNumberReadable((u256)0x7ffffff) = "2**27 - 1"
/// @example formatNumberReadable(-57896044618658097711785492504343953926634992332820282019728792003956564819968) = -2**255
template <class T>
inline std::string formatUnsignedNumberReadable (
inline std::string formatNumberReadable(
T const& _value,
bool _useTruncation = false
)
{
static_assert(
std::is_same<bigint, T>::value || !std::numeric_limits<T>::is_signed,
"only unsigned types or bigint supported"
); //bigint does not carry sign bit on shift
std::numeric_limits<T>::is_integer,
"only integer types are supported"
);

bool isNegative = _value < 0;
bigint temp;
bigint signedValue = isNegative ? (bigint(-1) * _value) : bigint(_value);
std::string sign = isNegative ? "-" : "";

// smaller numbers return as decimal
if (_value <= 0x1000000)
return _value.str();
if (signedValue <= 0x1000000)
return sign + signedValue.str();

HexCase hexcase = HexCase::Mixed;
HexPrefix prefix = HexPrefix::Add;
HexPrefix hexprefix = HexPrefix::Add;

// when multiple trailing zero bytes, format as N * 2**x
int i = 0;
T v = _value;
for (; (v & 0xff) == 0; v >>= 8)
for (temp = signedValue; (temp & 0xff) == 0; temp >>= 8)
++i;
if (i > 2)
{
// 0x100 yields 2**8 (N is 1 and redundant)
if (v == 1)
return "2**" + std::to_string(i * 8);
return toHex(toCompactBigEndian(v), prefix, hexcase) +
" * 2**" +
std::to_string(i * 8);
if (temp == 1)
return sign + "2**" + std::to_string(i * 8);
else if ((temp & (temp-1)) == 0)
{
int j = 0;
for (; (temp & 0x1) == 0; temp >>= 1)
j++;
return sign + "2**" + std::to_string(i * 8 + j);
}
else
return sign +
toHex(toCompactBigEndian(temp), hexprefix, hexcase) +
" * 2**" +
std::to_string(i * 8);
}

// when multiple trailing FF bytes, format as N * 2**x - 1
// when multiple trailing FF bytes, format as N * 2**x - 1 (if positive)
// or -N * 2**x + 1 (if negative)
i = 0;
for (v = _value; (v & 0xff) == 0xff; v >>= 8)
for (temp = signedValue; (temp & 0xff) == 0xff; temp >>= 8)
++i;
if (i > 2)
{
// 0xFF yields 2**8 - 1 (v is 0 in that case)
if (v == 0)
return "2**" + std::to_string(i * 8) + " - 1";
return toHex(toCompactBigEndian(T(v + 1)), prefix, hexcase) +
" * 2**" + std::to_string(i * 8) +
" - 1";
std::string suffix = isNegative ? " + 1" : " - 1";

if (temp == 0)
return sign + "2**" + std::to_string(i * 8) + suffix;
else if ((temp & (temp + 1)) == 0)
{
int j = 0;
for (; (temp & 0x1) != 0; temp >>= 1)
j++;
return sign + "2**" + std::to_string(i * 8 + j) + suffix;
}
else
return sign +
toHex(toCompactBigEndian(bigint(temp + 1)), hexprefix, hexcase) +
" * 2**" + std::to_string(i * 8) +
suffix;
}

std::string str = toHex(toCompactBigEndian(_value), prefix, hexcase);
std::string str = toHex(toCompactBigEndian(signedValue), hexprefix, hexcase);

if (_useTruncation)
{
// return as interior-truncated hex.
size_t len = str.size();

if (len < 24)
return str;
return sign + str;

size_t const initialChars = (prefix == HexPrefix::Add) ? 6 : 4;
size_t const initialChars = (hexprefix == HexPrefix::Add) ? 6 : 4;
size_t const finalChars = 4;
size_t numSkipped = len - initialChars - finalChars;

return str.substr(0, initialChars) +
return sign +
str.substr(0, initialChars) +
"...{+" +
std::to_string(numSkipped) +
" more}..." +
str.substr(len-finalChars, len);
}

// otherwise, show whole value.
return str;
}

/// Formats large numbers to be easily readable by humans.
/// Returns decimal representation for smaller numbers; hex for large numbers.
/// "Special" numbers, powers-of-two and powers-of-two minus 1, are returned in
/// formulaic form like 0x01 * 2**24 - 1.
/// @a T can be any integer variable, will typically be u160, u256 or bigint.
/// @param _value to be formatted
/// @param _useTruncation if true, internal truncation is also applied,
/// like 0x5555...{+56 more}...5555
/// @example formatNumberReadable((u256)0x7ffffff) = "0x08 * 2**24"
/// @example formatNumberReadable(-57896044618658097711785492504343953926634992332820282019728792003956564819968) = -0x80 * 2**248
template <class T>
inline std::string formatNumberReadable(
T const& _value,
bool _useTruncation = false
)
{
static_assert(
std::numeric_limits<T>::is_integer,
"only integer numbers are supported"
);

if (_value >= 0)
{
bigint const _v = bigint(_value);
return formatUnsignedNumberReadable(_v, _useTruncation);
}
else
{
bigint const _abs_value = bigint(-1) * _value;
return "-" + formatUnsignedNumberReadable(_abs_value, _useTruncation);
}
return sign + str;
}

/// Safely converts an unsigned integer as string into an unsigned int type.
Expand Down
2 changes: 1 addition & 1 deletion test/libsolidity/smtCheckerTests/operators/division_3.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ contract C {
// ====
// SMTEngine: all
// ----
// Warning 4984: (95-100): CHC: Overflow (resulting value larger than 0x80 * 2**248 - 1) happens here.\nCounterexample:\n\nx = (- 57896044618658097711785492504343953926634992332820282019728792003956564819968)\ny = (- 1)\n = 0\n\nTransaction trace:\nC.constructor()\nC.f((- 57896044618658097711785492504343953926634992332820282019728792003956564819968), (- 1))
// Warning 4984: (95-100): CHC: Overflow (resulting value larger than 2**255 - 1) happens here.\nCounterexample:\n\nx = (- 57896044618658097711785492504343953926634992332820282019728792003956564819968)\ny = (- 1)\n = 0\n\nTransaction trace:\nC.constructor()\nC.f((- 57896044618658097711785492504343953926634992332820282019728792003956564819968), (- 1))
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ contract C {
// SMTEngine: all
// SMTIgnoreCex: yes
// ----
// Warning 4984: (77-82): CHC: Overflow (resulting value larger than 0x80 * 2**248 - 1) happens here.
// Warning 4984: (77-82): CHC: Overflow (resulting value larger than 2**255 - 1) happens here.
// Warning 4281: (77-82): CHC: Division by zero happens here.
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ contract C {
// SMTEngine: all
// SMTIgnoreOS: macos
// ----
// Warning 4984: (96-101): CHC: Overflow (resulting value larger than 0x80 * 2**248 - 1) happens here.\nCounterexample:\n\nx = 57896044618658097711785492504343953926634992332820282019728792003956564819967\ny = (- 1)\n = 0\n\nTransaction trace:\nC.constructor()\nC.f(57896044618658097711785492504343953926634992332820282019728792003956564819967, (- 1))
// Warning 4984: (96-101): CHC: Overflow (resulting value larger than 2**255 - 1) happens here.\nCounterexample:\n\nx = 57896044618658097711785492504343953926634992332820282019728792003956564819967\ny = (- 1)\n = 0\n\nTransaction trace:\nC.constructor()\nC.f(57896044618658097711785492504343953926634992332820282019728792003956564819967, (- 1))
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@ contract C {
// SMTEngine: all
// SMTIgnoreCex: yes
// ----
// Warning 3944: (78-83): CHC: Underflow (resulting value less than -0x80 * 2**248) happens here.
// Warning 4984: (78-83): CHC: Overflow (resulting value larger than 0x80 * 2**248 - 1) happens here.
// Warning 3944: (78-83): CHC: Underflow (resulting value less than -2**255) happens here.
// Warning 4984: (78-83): CHC: Overflow (resulting value larger than 2**255 - 1) happens here.
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ contract C {
// SMTEngine: all
// SMTIgnoreCex: yes
// ----
// Warning 3944: (77-82): CHC: Underflow (resulting value less than -0x80 * 2**248) happens here.
// Warning 4984: (77-82): CHC: Overflow (resulting value larger than 0x80 * 2**248 - 1) happens here.
// Warning 3944: (77-82): CHC: Underflow (resulting value less than -2**255) happens here.
// Warning 4984: (77-82): CHC: Overflow (resulting value larger than 2**255 - 1) happens here.
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ contract C {
// SMTEngine: all
// SMTIgnoreCex: yes
// ----
// Warning 3944: (77-82): CHC: Underflow (resulting value less than -0x80 * 2**248) happens here.
// Warning 4984: (77-82): CHC: Overflow (resulting value larger than 0x80 * 2**248 - 1) happens here.
// Warning 3944: (77-82): CHC: Underflow (resulting value less than -2**255) happens here.
// Warning 4984: (77-82): CHC: Overflow (resulting value larger than 2**255 - 1) happens here.
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ contract C {
// SMTEngine: all
// SMTIgnoreCex: yes
// ----
// Warning 3944: (77-82): CHC: Underflow (resulting value less than -0x80 * 2**248) happens here.
// Warning 4984: (77-82): CHC: Overflow (resulting value larger than 0x80 * 2**248 - 1) happens here.
// Warning 3944: (77-82): CHC: Underflow (resulting value less than -2**255) happens here.
// Warning 4984: (77-82): CHC: Overflow (resulting value larger than 2**255 - 1) happens here.
2 changes: 1 addition & 1 deletion test/libsolidity/smtCheckerTests/unchecked/signed_mod.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@ contract C {
// SMTEngine: all
// ----
// Warning 4281: (85-90): CHC: Division by zero happens here.\nCounterexample:\n\na = 0\nb = 0\n = 0\n\nTransaction trace:\nC.constructor()\nC.f(0, 0)
// Warning 4984: (242-248): CHC: Overflow (resulting value larger than 0x80 * 2**248 - 1) happens here.\nCounterexample:\n\n_check = true\n = 0\nx = (- 57896044618658097711785492504343953926634992332820282019728792003956564819968)\n\nTransaction trace:\nC.constructor()\nC.g(true)
// Warning 4984: (242-248): CHC: Overflow (resulting value larger than 2**255 - 1) happens here.\nCounterexample:\n\n_check = true\n = 0\nx = (- 57896044618658097711785492504343953926634992332820282019728792003956564819968)\n\nTransaction trace:\nC.constructor()\nC.g(true)
66 changes: 41 additions & 25 deletions test/libsolutil/StringUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,17 +107,23 @@ BOOST_AUTO_TEST_CASE(test_human_readable_join)

BOOST_AUTO_TEST_CASE(test_format_number_readable)
{
BOOST_CHECK_EQUAL(formatNumberReadable(u256(0x8000000)), "0x08 * 2**24");
BOOST_CHECK_EQUAL(formatNumberReadable(u256(0x80000000)), "0x80 * 2**24");
BOOST_CHECK_EQUAL(formatNumberReadable(u256(0x800000000)), "0x08 * 2**32");
BOOST_CHECK_EQUAL(formatNumberReadable(u256(0x8000000000)), "0x80 * 2**32");
BOOST_CHECK_EQUAL(formatNumberReadable(u256(0x80000000000)), "0x08 * 2**40");

BOOST_CHECK_EQUAL(formatNumberReadable(u256(0x7ffffff)), "0x08 * 2**24 - 1");
BOOST_CHECK_EQUAL(formatNumberReadable(u256(0x7fffffff)), "0x80 * 2**24 - 1");
BOOST_CHECK_EQUAL(formatNumberReadable(u256(0x7ffffffff)), "0x08 * 2**32 - 1");
BOOST_CHECK_EQUAL(formatNumberReadable(u256(0x7fffffffff)), "0x80 * 2**32 - 1");
BOOST_CHECK_EQUAL(formatNumberReadable(u256(0x7ffffffffff)), "0x08 * 2**40 - 1");
BOOST_CHECK_EQUAL(formatNumberReadable(u256(0x10000000)), "2**28");
BOOST_CHECK_EQUAL(formatNumberReadable(u256(0x100000000)), "2**32");

BOOST_CHECK_EQUAL(formatNumberReadable(u256(0x8000000)), "2**27");
BOOST_CHECK_EQUAL(formatNumberReadable(u256(0x80000000)), "2**31");
BOOST_CHECK_EQUAL(formatNumberReadable(u256(0x800000000)), "2**35");
BOOST_CHECK_EQUAL(formatNumberReadable(u256(0x8000000000)), "2**39");
BOOST_CHECK_EQUAL(formatNumberReadable(u256(0x80000000000)), "2**43");

BOOST_CHECK_EQUAL(formatNumberReadable(u256(0x7ffffff)), "2**27 - 1");
BOOST_CHECK_EQUAL(formatNumberReadable(u256(0x7fffffff)), "2**31 - 1");
BOOST_CHECK_EQUAL(formatNumberReadable(u256(0x7ffffffff)), "2**35 - 1");
BOOST_CHECK_EQUAL(formatNumberReadable(u256(0x7fffffffff)), "2**39 - 1");
BOOST_CHECK_EQUAL(formatNumberReadable(u256(0x7ffffffffff)), "2**43 - 1");

BOOST_CHECK_EQUAL(formatNumberReadable(u256(0xfffffff)), "2**28 - 1");
BOOST_CHECK_EQUAL(formatNumberReadable(u256(0xffffffff)), "2**32 - 1");

BOOST_CHECK_EQUAL(formatNumberReadable(u256(0x88000000)), "0x88 * 2**24");
BOOST_CHECK_EQUAL(formatNumberReadable(u256(0x8888888888000000)), "0x8888888888 * 2**24");
Expand All @@ -142,6 +148,10 @@ BOOST_AUTO_TEST_CASE(test_format_number_readable)
BOOST_CHECK_EQUAL(formatNumberReadable(c, true), "0xABCD...{+56 more}...6789");
BOOST_CHECK_EQUAL(formatNumberReadable(d, true), "0xAAAAaaaaAAAAaaab * 2**192 - 1");

BOOST_CHECK_EQUAL(formatNumberReadable(u256(0)), "0");
BOOST_CHECK_EQUAL(formatNumberReadable(u256(0x10000)), "65536");
BOOST_CHECK_EQUAL(formatNumberReadable(u256(0xFFFF)), "65535");

//for codegen/ExpressionCompiler
BOOST_CHECK_EQUAL(formatNumberReadable(u256(-1)), "2**256 - 1");

Expand All @@ -154,17 +164,23 @@ BOOST_AUTO_TEST_CASE(test_format_number_readable)

BOOST_AUTO_TEST_CASE(test_format_number_readable_signed)
{
BOOST_CHECK_EQUAL(formatNumberReadable((-1) * s256(0x8000000)), "-0x08 * 2**24");
BOOST_CHECK_EQUAL(formatNumberReadable((-1) * s256(0x80000000)), "-0x80 * 2**24");
BOOST_CHECK_EQUAL(formatNumberReadable((-1) * s256(0x800000000)), "-0x08 * 2**32");
BOOST_CHECK_EQUAL(formatNumberReadable((-1) * s256(0x8000000000)), "-0x80 * 2**32");
BOOST_CHECK_EQUAL(formatNumberReadable((-1) * s256(0x80000000000)), "-0x08 * 2**40");

BOOST_CHECK_EQUAL(formatNumberReadable((-1) * s256(0x7ffffff)), "-0x08 * 2**24 - 1");
BOOST_CHECK_EQUAL(formatNumberReadable((-1) * s256(0x7fffffff)), "-0x80 * 2**24 - 1");
BOOST_CHECK_EQUAL(formatNumberReadable((-1) * s256(0x7ffffffff)), "-0x08 * 2**32 - 1");
BOOST_CHECK_EQUAL(formatNumberReadable((-1) * s256(0x7fffffffff)), "-0x80 * 2**32 - 1");
BOOST_CHECK_EQUAL(formatNumberReadable((-1) * s256(0x7ffffffffff)), "-0x08 * 2**40 - 1");
BOOST_CHECK_EQUAL(formatNumberReadable((-1) * s256(0x10000000)), "-2**28");
BOOST_CHECK_EQUAL(formatNumberReadable((-1) * s256(0x100000000)), "-2**32");

BOOST_CHECK_EQUAL(formatNumberReadable((-1) * s256(0x8000000)), "-2**27");
BOOST_CHECK_EQUAL(formatNumberReadable((-1) * s256(0x80000000)), "-2**31");
BOOST_CHECK_EQUAL(formatNumberReadable((-1) * s256(0x800000000)), "-2**35");
BOOST_CHECK_EQUAL(formatNumberReadable((-1) * s256(0x8000000000)), "-2**39");
BOOST_CHECK_EQUAL(formatNumberReadable((-1) * s256(0x80000000000)), "-2**43");

BOOST_CHECK_EQUAL(formatNumberReadable((-1) * s256(0x7ffffff)), "-2**27 + 1");
BOOST_CHECK_EQUAL(formatNumberReadable((-1) * s256(0x7fffffff)), "-2**31 + 1");
BOOST_CHECK_EQUAL(formatNumberReadable((-1) * s256(0x7ffffffff)), "-2**35 + 1");
BOOST_CHECK_EQUAL(formatNumberReadable((-1) * s256(0x7fffffffff)), "-2**39 + 1");
BOOST_CHECK_EQUAL(formatNumberReadable((-1) * s256(0x7ffffffffff)), "-2**43 + 1");

BOOST_CHECK_EQUAL(formatNumberReadable((-1) * s256(0xfffffff)), "-2**28 + 1");
BOOST_CHECK_EQUAL(formatNumberReadable((-1) * s256(0xffffffff)), "-2**32 + 1");

BOOST_CHECK_EQUAL(formatNumberReadable((-1) * s256(0x88000000)), "-0x88 * 2**24");
BOOST_CHECK_EQUAL(formatNumberReadable((-1) * s256(0x8888888888000000)), "-0x8888888888 * 2**24");
Expand All @@ -190,7 +206,7 @@ BOOST_AUTO_TEST_CASE(test_format_number_readable_signed)

BOOST_CHECK_EQUAL(formatNumberReadable(b, true), "-0x5555...{+56 more}...5555");
BOOST_CHECK_EQUAL(formatNumberReadable(c, true), "-0x0BCD...{+56 more}...6789");
BOOST_CHECK_EQUAL(formatNumberReadable(d, true), "-0x5555555555555556 * 2**192 - 1");
BOOST_CHECK_EQUAL(formatNumberReadable(d, true), "-0x5555555555555556 * 2**192 + 1");

BOOST_CHECK_EQUAL(formatNumberReadable(s256(-1)), "-1");
BOOST_CHECK_EQUAL(formatNumberReadable((-1) * s256(0x10000)), "-65536");
Expand All @@ -200,13 +216,13 @@ BOOST_AUTO_TEST_CASE(test_format_number_readable_signed)
formatNumberReadable(
frontend::IntegerType(256, frontend::IntegerType::Modifier::Signed).minValue()
),
"-0x80 * 2**248"
"-2**255"
);
BOOST_CHECK_EQUAL(
formatNumberReadable(
frontend::IntegerType(256, frontend::IntegerType::Modifier::Signed).maxValue()
),
"0x80 * 2**248 - 1"
"2**255 - 1"
);
}

Expand Down

0 comments on commit 9feab34

Please sign in to comment.