mirror of
https://github.com/XRPLF/rippled.git
synced 2026-04-29 15:37:57 +00:00
281 lines
7.2 KiB
C++
281 lines
7.2 KiB
C++
#include <xrpl/beast/core/LexicalCast.h>
|
|
#include <xrpl/beast/unit_test/suite.h>
|
|
#include <xrpl/beast/xor_shift_engine.h>
|
|
|
|
#include <cstdint>
|
|
#include <limits>
|
|
#include <sstream>
|
|
#include <string>
|
|
|
|
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 = 0;
|
|
|
|
expect(lexicalCastChecked(out, "-0"), "0");
|
|
expect(lexicalCastChecked(out, "0"), "0");
|
|
expect(lexicalCastChecked(out, "+0"), "0");
|
|
}
|
|
|
|
{
|
|
std::uint32_t out = 0;
|
|
|
|
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 const 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
|