2
0
mirror of https://github.com/boostorg/json.git synced 2026-02-02 21:02:18 +00:00
Files
json/test/_detail_number.cpp
Richard Hodges 5aae31dc74 number parser work
close #33
2020-01-14 13:43:07 -08:00

715 lines
18 KiB
C++

//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/vinniefalco/json
//
// Test that header file is self-contained.
#include <boost/json/detail/number.hpp>
#include <cstdlib>
#include <iomanip>
#include <type_traits>
#include "test_suite.hpp"
#define ACCURATE_CONVERSION 0
namespace boost {
namespace json {
namespace detail {
bool
operator==(
number const& lhs,
number const& rhs) noexcept
{
if(lhs.kind != rhs.kind)
return false;
switch(lhs.kind)
{
case json::kind::int64:
return lhs.i == rhs.i;
case json::kind::uint64:
return lhs.u == rhs.u;
default:
break;
}
return
std::signbit(lhs.d) ==
std::signbit(rhs.d) &&
lhs.d == rhs.d;
}
struct double_diagnoser
{
static void emit_hex(std::ostream& os, double d)
{
std::uint64_t binary;
static_assert(sizeof(binary) == sizeof(d), "");
std::memcpy(&binary, &d, sizeof(d));
auto oldflags = os.flags();
try
{
os << std::hex << std::setw(16) << std::setfill('0') << binary;
os.flags(oldflags);
}
catch(...)
{
os.flags(oldflags);
}
}
static void emit_pow2(std::ostream& os, double d)
{
auto oldflags = os.flags();
try
{
int exponent = 0;
auto mantissa = std::frexp(d, &exponent);
os << std::fixed << mantissa;
os << " *2^ ";
os << exponent;
os.flags(oldflags);
}
catch(...)
{
os.flags(oldflags);
throw;
}
}
static void emit_scientific(std::ostream& os, double d)
{
auto oldflags = os.flags();
try
{
os
<< std::setprecision(std::numeric_limits<double>::max_digits10)
<< d;
os.flags(oldflags);
}
catch(...)
{
os.flags(oldflags);
throw;
}
}
friend
std::ostream&
operator<<(std::ostream& os, double_diagnoser diag)
{
emit_scientific(os, diag.d);
os << "(0x";
emit_hex(os, diag.d);
os << ") ";
emit_pow2(os, diag.d);
return os;
}
double d;
};
auto diagnose(double d) -> double_diagnoser
{
return double_diagnoser { d };
}
bool
are_close(
double x,
double y)
{
std::uint64_t bx, by;
std::memcpy(&bx, &x, sizeof(x));
std::memcpy(&by, &y, sizeof(y));
auto diff = bx - by;
switch (diff)
{
case 0:
case 1:
case 0xffffffffffffffff:
return true;
default:
break;
}
return false;
}
class number_test
{
public:
test_suite::log_type log;
template<class F>
static
void
grind(
string_view s,
F&& f)
{
{
error_code ec;
number_parser p;
p.write(s.data(), s.size(), ec);
if(BOOST_TEST(! ec))
{
BOOST_TEST(p.is_done());
f(p.get());
}
}
for(;;)
{
error_code ec;
number_parser p;
p.write_some(s.data(), s.size(), ec);
if(! BOOST_TEST(! ec))
break;
auto n = p.write_some("x", 1, ec);
if(! BOOST_TEST(
p.is_done()))
break;
if(! BOOST_TEST(n == 0))
break;
f(p.get());
p.finish(ec);
BOOST_TEST(! ec);
break;
}
for(size_t i = 1; i < s.size(); ++i)
{
error_code ec;
number_parser p;
p.write_some(s.data(), i, ec);
if(! BOOST_TEST(! ec))
continue;
p.write(
s.data() + i, s.size() - i, ec);
if(! BOOST_TEST(! ec))
continue;
BOOST_TEST(p.is_done());
f(p.get());
}
}
//------------------------------------------------------
void
testMembers()
{
// maybe_init
{
number_parser p;
BOOST_TEST(! p.maybe_init(0));
BOOST_TEST(! p.maybe_init('A'));
BOOST_TEST(! p.maybe_init('a'));
BOOST_TEST(! p.maybe_init('.'));
BOOST_TEST(! p.maybe_init('!'));
BOOST_TEST(! p.maybe_init(' '));
BOOST_TEST(p.maybe_init('0')); p.reset();
BOOST_TEST(p.maybe_init('1')); p.reset();
BOOST_TEST(p.maybe_init('2')); p.reset();
BOOST_TEST(p.maybe_init('3')); p.reset();
BOOST_TEST(p.maybe_init('4')); p.reset();
BOOST_TEST(p.maybe_init('5')); p.reset();
BOOST_TEST(p.maybe_init('6')); p.reset();
BOOST_TEST(p.maybe_init('7')); p.reset();
BOOST_TEST(p.maybe_init('8')); p.reset();
BOOST_TEST(p.maybe_init('9')); p.reset();
BOOST_TEST(p.maybe_init('0')); p.reset();
BOOST_TEST(p.maybe_init('-')); p.reset();
}
// finish
{
error_code ec;
number_parser p;
p.write_some("0x", 2, ec);
if(BOOST_TEST(! ec))
{
p.finish(ec);
BOOST_TEST(! ec);
}
}
}
//------------------------------------------------------
void
check_bad(string_view s)
{
error_code xec;
{
number_parser p;
p.write(s.data(), s.size(), xec);
BOOST_TEST(xec);
}
for(size_t i = 1; i < s.size(); ++i)
{
error_code ec;
number_parser p;
p.write_some(s.data(), i, ec);
if(ec == xec)
{
BOOST_TEST_PASS();
continue;
}
if(! BOOST_TEST(! ec))
continue;
p.write(
s.data() + i, s.size() - i, ec);
BOOST_TEST(ec == xec);
}
}
void
check_int64(
string_view s,
int64_t i)
{
grind(s,
[&](number num)
{
if( BOOST_TEST(
num.kind == kind::int64))
BOOST_TEST(num.i == i);
});
}
void
check_uint64(
string_view s,
uint64_t u)
{
grind(s,
[&](number num)
{
if( BOOST_TEST(
num.kind == kind::uint64))
BOOST_TEST(num.u == u);
});
}
void
testIntegers()
{
check_int64( "-9223372036854775808", INT64_MIN);
check_int64( "-9223372036854775807", -9223372036854775807);
check_int64( "-999999999999999999", -999999999999999999);
check_int64( "-99999999999999999", -99999999999999999);
check_int64( "-9999999999999999", -9999999999999999);
check_int64( "-999999999999999", -999999999999999);
check_int64( "-99999999999999", -99999999999999);
check_int64( "-9999999999999", -9999999999999);
check_int64( "-999999999999", -999999999999);
check_int64( "-99999999999", -99999999999);
check_int64( "-9999999999", -9999999999);
check_int64( "-999999999", -999999999);
check_int64( "-99999999", -99999999);
check_int64( "-9999999", -9999999);
check_int64( "-999999", -999999);
check_int64( "-99999", -99999);
check_int64( "-9999", -9999);
check_int64( "-999", -999);
check_int64( "-99", -99);
check_int64( "-9", -9);
check_int64( "0", 0);
check_int64( "9", 9);
check_int64( "99", 99);
check_int64( "999", 999);
check_int64( "9999", 9999);
check_int64( "99999", 99999);
check_int64( "999999", 999999);
check_int64( "9999999", 9999999);
check_int64( "99999999", 99999999);
check_int64( "999999999", 999999999);
check_int64( "9999999999", 9999999999);
check_int64( "99999999999", 99999999999);
check_int64( "999999999999", 999999999999);
check_int64( "9999999999999", 9999999999999);
check_int64( "99999999999999", 99999999999999);
check_int64( "999999999999999", 999999999999999);
check_int64( "9999999999999999", 9999999999999999);
check_int64( "99999999999999999", 99999999999999999);
check_int64( "999999999999999999", 999999999999999999);
check_int64( "9223372036854775807", INT64_MAX);
check_uint64( "9223372036854775808", 9223372036854775808ULL);
check_uint64( "9999999999999999999", 9999999999999999999ULL);
check_uint64( "18446744073709551615", UINT64_MAX);
}
void
testBad()
{
check_bad("");
check_bad("x");
check_bad("e");
check_bad("1ex");
check_bad("-");
check_bad("1a");
check_bad(".");
check_bad("-.");
check_bad("1.");
check_bad("-1.");
check_bad("1.x");
check_bad("1+");
check_bad("1-");
check_bad("0.0+");
check_bad("0.0e+");
check_bad("0.0e-");
check_bad("0.0e0-");
check_bad("0.0e");
check_bad("0.e1");
check_bad("-e");
check_bad("-x");
check_bad("2.e+3");
check_bad("-2.e+3");
// leading 0 must be followed by [.eE] or nothing
check_bad( "00");
check_bad( "01");
check_bad( "00.");
check_bad( "00.0");
check_bad("-00");
check_bad("-01");
check_bad("-00.");
check_bad("-00.0");
}
//------------------------------------------------------
struct f_boost
{
static
string_view
name() noexcept
{
return "boost";
}
double
operator()(string_view s) const
{
error_code ec;
number_parser p;
p.write(s.data(), s.size(), ec);
if(ec)
BOOST_THROW_EXCEPTION(
system_error(ec));
BOOST_TEST(p.is_done());
auto const num = p.get();
BOOST_ASSERT(
num.kind == kind::double_);
grind(s,
[&](number num1)
{
if( BOOST_TEST(
num1.kind == kind::double_))
BOOST_TEST(num1.d == num.d);
});
return num.d;
}
};
// Verify that f converts to the
// same double produced by `strtod`.
// Requires `s` is not represented by an integral type.
template<class F>
void
fcheck(std::string const& s, F const& f)
{
char* str_end;
double const need =
std::strtod(s.c_str(), &str_end);
BOOST_TEST(str_end == &s.back() + 1);
double const got = f(s);
auto same = got == need;
#if !ACCURATE_CONVERSION
auto close = same ? true : are_close(got, need);
if(! BOOST_TEST(close))
{
log << "not close : " <<
f.name() << "\n"
"string: " << s << "\n"
"need : " << diagnose(need) << "\n"
"got : " << diagnose(got) <<
std::endl;
}
#else
if (!BOOST_TEST(same))
{
log << "close but not close enough : " <<
f.name() << "\n"
"string: " << s << "\n"
"need : " << diagnose(need) << "\n"
"got : " << diagnose(got) <<
std::endl;
}
#endif
}
template<class F>
void
check_numbers(F const& f)
{
auto const fc =
[&](std::string const& s)
{
fcheck(s, f);
};
fc("-999999999999999999999");
fc("-100000000000000000009");
fc("-10000000000000000000");
fc("-9223372036854775809");
fc("18446744073709551616");
fc("99999999999999999999");
fc("999999999999999999999");
fc("1000000000000000000000");
fc("9999999999999999999999");
fc("99999999999999999999999");
fc("-0.9999999999999999999999");
fc("-0.9999999999999999");
fc("-0.9007199254740991");
fc("-0.999999999999999");
fc("-0.99999999999999");
fc("-0.9999999999999");
fc("-0.999999999999");
fc("-0.99999999999");
fc("-0.9999999999");
fc("-0.999999999");
fc("-0.99999999");
fc("-0.9999999");
fc("-0.999999");
fc("-0.99999");
fc("-0.9999");
fc("-0.8125");
fc("-0.999");
fc("-0.99");
fc("-1.0");
fc("-0.9");
fc("-0.0");
fc("0.0");
fc("0.9");
fc("0.99");
fc("0.999");
fc("0.8125");
fc("0.9999");
fc("0.99999");
fc("0.999999");
fc("0.9999999");
fc("0.99999999");
fc("0.999999999");
fc("0.9999999999");
fc("0.99999999999");
fc("0.999999999999");
fc("0.9999999999999");
fc("0.99999999999999");
fc("0.999999999999999");
fc("0.9007199254740991");
fc("0.9999999999999999");
fc("0.9999999999999999999999");
fc("0.999999999999999999999999999");
fc("-1e308");
fc("-1e-308");
fc("-9999e300");
fc("-999e100");
fc("-99e10");
fc("-9e1");
fc("9e1");
fc("99e10");
fc("999e100");
fc("9999e300");
fc("999999999999999999.0");
fc("999999999999999999999.0");
fc("999999999999999999999e5");
fc("999999999999999999999.0e5");
fc("0.00000000000000001");
fc("-1e-1");
fc("-1e0");
fc("-1e1");
fc("0e0");
fc("1e0");
fc("1e10");
fc("0."
"00000000000000000000000000000000000000000000000000" // 50 zeroes
"1e50");
fc("-0."
"00000000000000000000000000000000000000000000000000" // 50 zeroes
"1e50");
fc("0."
"00000000000000000000000000000000000000000000000000"
"00000000000000000000000000000000000000000000000000"
"00000000000000000000000000000000000000000000000000"
"00000000000000000000000000000000000000000000000000"
"00000000000000000000000000000000000000000000000000"
"00000000000000000000000000000000000000000000000000"
"00000000000000000000000000000000000000000000000000"
"00000000000000000000000000000000000000000000000000"
"00000000000000000000000000000000000000000000000000"
"00000000000000000000000000000000000000000000000000" // 500 zeroes
"1e600");
fc("-0."
"00000000000000000000000000000000000000000000000000"
"00000000000000000000000000000000000000000000000000"
"00000000000000000000000000000000000000000000000000"
"00000000000000000000000000000000000000000000000000"
"00000000000000000000000000000000000000000000000000"
"00000000000000000000000000000000000000000000000000"
"00000000000000000000000000000000000000000000000000"
"00000000000000000000000000000000000000000000000000"
"00000000000000000000000000000000000000000000000000"
"00000000000000000000000000000000000000000000000000" // 500 zeroes
"1e600");
fc("0e"
"00000000000000000000000000000000000000000000000000"
"00000000000000000000000000000000000000000000000000"
"00000000000000000000000000000000000000000000000000"
"00000000000000000000000000000000000000000000000000"
"00000000000000000000000000000000000000000000000000"
"00000000000000000000000000000000000000000000000000"
"00000000000000000000000000000000000000000000000000"
"00000000000000000000000000000000000000000000000000"
"00000000000000000000000000000000000000000000000000"
"00000000000000000000000000000000000000000000000000" // 500 zeroes
);
}
void
testDoubles()
{
check_numbers(f_boost{});
}
static
number
int64_num(int64_t i) noexcept
{
number num;
num.i = i;
num.kind = kind::int64;
return num;
}
static
number
uint64_num(uint64_t u) noexcept
{
number num;
num.u = u;
num.kind = kind::uint64;
return num;
}
static
number
double_num(double d) noexcept
{
number num;
num.d = d;
num.kind = kind::double_;
return num;
}
void
testEdgeCases()
{
auto const parse =
[&](string_view s)
{
error_code ec;
number_parser p;
p.write(s.data(), s.size(), ec);
BOOST_TEST(! ec);
return p.get();
};
BOOST_TEST(parse("-0.0") == double_num(-0.0));
BOOST_TEST(parse("-0E0") == double_num(-0.0));
BOOST_TEST(parse("-0") == int64_num(0));
BOOST_TEST(parse("0") == int64_num(0));
BOOST_TEST(parse("0.010") == double_num(0.01));
BOOST_TEST(parse("-0.010") == double_num(-0.01));
BOOST_TEST(parse("1.010") == double_num(1.01));
BOOST_TEST(parse("-1.010") == double_num(-1.01));
}
void
run()
{
testEdgeCases();
testMembers();
testIntegers();
testBad();
testDoubles();
}
};
TEST_SUITE(number_test, "boost.json.detail.number");
} // detail
} // json
} // boost
#if 0
(for positive)
A. accumulate digits into unsigned u
if(got('.'))
if( have_dot )
return error;
dot_pos = pos
else if(u > UINT64_MAX)
goto state C
else
++dig_;
accumulate digit
(for negative)
B. accumulate digits into unsigned u
if(got('.'))
if( have_dot )
return error;
dot_pos = pos
else if(u > abs(INT64_MIN))
goto state C
else
++dig_;
accumulate digit
C. accumulate exponent offset
if(got('e', 'E', '-', '+')
...
else if(got('.'))
if( have_dot )
return error;
dot_pos = pos
else
if( have_dot )
// do nothing
else
++dig_;
#endif