mirror of
https://github.com/XRPLF/rippled.git
synced 2026-04-29 15:37:57 +00:00
Per XLS-0095, we are taking steps to rename ripple(d) to xrpl(d). This change specifically removes all copyright notices referencing Ripple, XRPLF, and certain affiliated contributors upon mutual agreement, so the notice in the LICENSE.md file applies throughout. Copyright notices referencing external contributions remain as-is. Duplicate verbiage is also removed.
276 lines
7.1 KiB
C++
276 lines
7.1 KiB
C++
#include <xrpl/beast/core/LexicalCast.h>
|
|
#include <xrpl/beast/unit_test.h>
|
|
#include <xrpl/beast/xor_shift_engine.h>
|
|
|
|
namespace beast {
|
|
|
|
class LexicalCast_test : public unit_test::suite
|
|
{
|
|
public:
|
|
template <class IntType>
|
|
static IntType
|
|
nextRandomInt(xor_shift_engine& r)
|
|
{
|
|
return static_cast<IntType>(r());
|
|
}
|
|
|
|
template <class IntType>
|
|
void
|
|
testInteger(IntType in)
|
|
{
|
|
std::string s;
|
|
IntType out(in + 1);
|
|
|
|
expect(lexicalCastChecked(s, in));
|
|
expect(lexicalCastChecked(out, s));
|
|
expect(out == in);
|
|
}
|
|
|
|
template <class IntType>
|
|
void
|
|
testIntegers(xor_shift_engine& r)
|
|
{
|
|
{
|
|
std::stringstream ss;
|
|
ss << "random " << typeid(IntType).name();
|
|
testcase(ss.str());
|
|
|
|
for (int i = 0; i < 1000; ++i)
|
|
{
|
|
IntType const value(nextRandomInt<IntType>(r));
|
|
testInteger(value);
|
|
}
|
|
}
|
|
|
|
{
|
|
std::stringstream ss;
|
|
ss << "numeric_limits <" << typeid(IntType).name() << ">";
|
|
testcase(ss.str());
|
|
|
|
testInteger(std::numeric_limits<IntType>::min());
|
|
testInteger(std::numeric_limits<IntType>::max());
|
|
}
|
|
}
|
|
|
|
void
|
|
testPathologies()
|
|
{
|
|
testcase("pathologies");
|
|
try
|
|
{
|
|
lexicalCastThrow<int>("\xef\xbc\x91\xef\xbc\x90"); // utf-8 encoded
|
|
}
|
|
catch (BadLexicalCast const&)
|
|
{
|
|
pass();
|
|
}
|
|
}
|
|
|
|
template <class T>
|
|
void
|
|
tryBadConvert(std::string const& s)
|
|
{
|
|
T out;
|
|
expect(!lexicalCastChecked(out, s), s);
|
|
}
|
|
|
|
void
|
|
testConversionOverflows()
|
|
{
|
|
testcase("conversion overflows");
|
|
|
|
tryBadConvert<std::uint64_t>("99999999999999999999");
|
|
tryBadConvert<std::uint32_t>("4294967300");
|
|
tryBadConvert<std::uint16_t>("75821");
|
|
}
|
|
|
|
void
|
|
testConversionUnderflows()
|
|
{
|
|
testcase("conversion underflows");
|
|
|
|
tryBadConvert<std::uint32_t>("-1");
|
|
|
|
tryBadConvert<std::int64_t>("-99999999999999999999");
|
|
tryBadConvert<std::int32_t>("-4294967300");
|
|
tryBadConvert<std::int16_t>("-75821");
|
|
}
|
|
|
|
template <class T>
|
|
bool
|
|
tryEdgeCase(std::string const& s)
|
|
{
|
|
T ret;
|
|
|
|
bool const result = lexicalCastChecked(ret, s);
|
|
|
|
if (!result)
|
|
return false;
|
|
|
|
return s == std::to_string(ret);
|
|
}
|
|
|
|
void
|
|
testEdgeCases()
|
|
{
|
|
testcase("conversion edge cases");
|
|
|
|
expect(tryEdgeCase<std::uint64_t>("18446744073709551614"));
|
|
expect(tryEdgeCase<std::uint64_t>("18446744073709551615"));
|
|
expect(!tryEdgeCase<std::uint64_t>("18446744073709551616"));
|
|
|
|
expect(tryEdgeCase<std::int64_t>("9223372036854775806"));
|
|
expect(tryEdgeCase<std::int64_t>("9223372036854775807"));
|
|
expect(!tryEdgeCase<std::int64_t>("9223372036854775808"));
|
|
|
|
expect(tryEdgeCase<std::int64_t>("-9223372036854775807"));
|
|
expect(tryEdgeCase<std::int64_t>("-9223372036854775808"));
|
|
expect(!tryEdgeCase<std::int64_t>("-9223372036854775809"));
|
|
|
|
expect(tryEdgeCase<std::uint32_t>("4294967294"));
|
|
expect(tryEdgeCase<std::uint32_t>("4294967295"));
|
|
expect(!tryEdgeCase<std::uint32_t>("4294967296"));
|
|
|
|
expect(tryEdgeCase<std::int32_t>("2147483646"));
|
|
expect(tryEdgeCase<std::int32_t>("2147483647"));
|
|
expect(!tryEdgeCase<std::int32_t>("2147483648"));
|
|
|
|
expect(tryEdgeCase<std::int32_t>("-2147483647"));
|
|
expect(tryEdgeCase<std::int32_t>("-2147483648"));
|
|
expect(!tryEdgeCase<std::int32_t>("-2147483649"));
|
|
|
|
expect(tryEdgeCase<std::uint16_t>("65534"));
|
|
expect(tryEdgeCase<std::uint16_t>("65535"));
|
|
expect(!tryEdgeCase<std::uint16_t>("65536"));
|
|
|
|
expect(tryEdgeCase<std::int16_t>("32766"));
|
|
expect(tryEdgeCase<std::int16_t>("32767"));
|
|
expect(!tryEdgeCase<std::int16_t>("32768"));
|
|
|
|
expect(tryEdgeCase<std::int16_t>("-32767"));
|
|
expect(tryEdgeCase<std::int16_t>("-32768"));
|
|
expect(!tryEdgeCase<std::int16_t>("-32769"));
|
|
}
|
|
|
|
template <class T>
|
|
void
|
|
testThrowConvert(std::string const& s, bool success)
|
|
{
|
|
bool result = !success;
|
|
T out;
|
|
|
|
try
|
|
{
|
|
out = lexicalCastThrow<T>(s);
|
|
result = true;
|
|
}
|
|
catch (BadLexicalCast const&)
|
|
{
|
|
result = false;
|
|
}
|
|
|
|
expect(result == success, s);
|
|
}
|
|
|
|
void
|
|
testThrowingConversions()
|
|
{
|
|
testcase("throwing conversion");
|
|
|
|
testThrowConvert<std::uint64_t>("99999999999999999999", false);
|
|
testThrowConvert<std::uint64_t>("9223372036854775806", true);
|
|
|
|
testThrowConvert<std::uint32_t>("4294967290", true);
|
|
testThrowConvert<std::uint32_t>("42949672900", false);
|
|
testThrowConvert<std::uint32_t>("429496729000", false);
|
|
testThrowConvert<std::uint32_t>("4294967290000", false);
|
|
|
|
testThrowConvert<std::int32_t>("5294967295", false);
|
|
testThrowConvert<std::int32_t>("-2147483644", true);
|
|
|
|
testThrowConvert<std::int16_t>("66666", false);
|
|
testThrowConvert<std::int16_t>("-5711", true);
|
|
}
|
|
|
|
void
|
|
testZero()
|
|
{
|
|
testcase("zero conversion");
|
|
|
|
{
|
|
std::int32_t out;
|
|
|
|
expect(lexicalCastChecked(out, "-0"), "0");
|
|
expect(lexicalCastChecked(out, "0"), "0");
|
|
expect(lexicalCastChecked(out, "+0"), "0");
|
|
}
|
|
|
|
{
|
|
std::uint32_t out;
|
|
|
|
expect(!lexicalCastChecked(out, "-0"), "0");
|
|
expect(lexicalCastChecked(out, "0"), "0");
|
|
expect(lexicalCastChecked(out, "+0"), "0");
|
|
}
|
|
}
|
|
|
|
void
|
|
testEntireRange()
|
|
{
|
|
testcase("entire range");
|
|
|
|
std::int32_t i = std::numeric_limits<std::int16_t>::min();
|
|
std::string const empty("");
|
|
|
|
while (i <= std::numeric_limits<std::int16_t>::max())
|
|
{
|
|
std::int16_t j = static_cast<std::int16_t>(i);
|
|
|
|
auto actual = std::to_string(j);
|
|
|
|
auto result = lexicalCast(j, empty);
|
|
|
|
expect(result == actual, actual + " (string to integer)");
|
|
|
|
if (result == actual)
|
|
{
|
|
auto number = lexicalCast<std::int16_t>(result);
|
|
|
|
if (number != j)
|
|
expect(false, actual + " (integer to string)");
|
|
}
|
|
|
|
i++;
|
|
}
|
|
}
|
|
|
|
void
|
|
run() override
|
|
{
|
|
std::int64_t const seedValue = 50;
|
|
|
|
xor_shift_engine r(seedValue);
|
|
|
|
testIntegers<int>(r);
|
|
testIntegers<unsigned int>(r);
|
|
testIntegers<short>(r);
|
|
testIntegers<unsigned short>(r);
|
|
testIntegers<std::int32_t>(r);
|
|
testIntegers<std::uint32_t>(r);
|
|
testIntegers<std::int64_t>(r);
|
|
testIntegers<std::uint64_t>(r);
|
|
|
|
testPathologies();
|
|
testConversionOverflows();
|
|
testConversionUnderflows();
|
|
testThrowingConversions();
|
|
testZero();
|
|
testEdgeCases();
|
|
testEntireRange();
|
|
}
|
|
};
|
|
|
|
BEAST_DEFINE_TESTSUITE(LexicalCast, beast, beast);
|
|
|
|
} // namespace beast
|