json: store numbers natively as uint/sint/real
allows more accurate representations and better error checking.
This commit is contained in:
parent
1f432c13b7
commit
8142944139
@ -183,22 +183,23 @@ json::flat::parse (const boost::filesystem::path &path)
|
|||||||
std::ostream&
|
std::ostream&
|
||||||
json::flat::operator<< (std::ostream &os, json::flat::type t)
|
json::flat::operator<< (std::ostream &os, json::flat::type t)
|
||||||
{
|
{
|
||||||
|
using T = json::flat::type;
|
||||||
|
|
||||||
switch (t) {
|
switch (t) {
|
||||||
case json::flat::type::STRING: os << "STRING"; break;
|
case T::STRING: return os << "STRING";
|
||||||
case json::flat::type::NUL: os << "NUL"; break;
|
case T::NUL: return os << "NUL";
|
||||||
case json::flat::type::BOOLEAN: os << "BOOLEAN"; break;
|
case T::BOOLEAN: return os << "BOOLEAN";
|
||||||
case json::flat::type::INTEGER: os << "INTEGER"; break;
|
case T::INTEGER: return os << "INTEGER";
|
||||||
case json::flat::type::REAL: os << "REAL"; break;
|
case T::REAL: return os << "REAL";
|
||||||
|
|
||||||
case json::flat::type::OBJECT_BEGIN: os << "OBJECT_BEGIN"; break;
|
case T::OBJECT_BEGIN: return os << "OBJECT_BEGIN";
|
||||||
case json::flat::type::OBJECT_END: os << "OBJECT_END"; break;
|
case T::OBJECT_END: return os << "OBJECT_END";
|
||||||
case json::flat::type::ARRAY_BEGIN: os << "ARRAY_BEGIN"; break;
|
case T::ARRAY_BEGIN: return os << "ARRAY_BEGIN";
|
||||||
case json::flat::type::ARRAY_END: os << "ARRAY_END"; break;
|
case T::ARRAY_END: return os << "ARRAY_END";
|
||||||
|
|
||||||
default:
|
case T::UNKNOWN: ;
|
||||||
unreachable ();
|
// fall out
|
||||||
}
|
}
|
||||||
|
unreachable ();
|
||||||
return os;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,7 +174,7 @@ validate (json::tree::string &node,
|
|||||||
// check length is less than a maximum
|
// check length is less than a maximum
|
||||||
auto maxLength = schema.find ("maxLength");
|
auto maxLength = schema.find ("maxLength");
|
||||||
if (maxLength != schema.cend ()) {
|
if (maxLength != schema.cend ()) {
|
||||||
auto cmp = maxLength->second->as_number ().native ();
|
auto cmp = maxLength->second->as_number ().uint ();
|
||||||
if (!util::is_integer (cmp))
|
if (!util::is_integer (cmp))
|
||||||
throw length_error ("maxLength");
|
throw length_error ("maxLength");
|
||||||
|
|
||||||
@ -185,7 +185,7 @@ validate (json::tree::string &node,
|
|||||||
// check length is greater than a maximum
|
// check length is greater than a maximum
|
||||||
auto minLength = schema.find ("minLength");
|
auto minLength = schema.find ("minLength");
|
||||||
if (minLength != schema.cend ()) {
|
if (minLength != schema.cend ()) {
|
||||||
auto cmp = minLength->second->as_number ().native ();
|
auto cmp = minLength->second->as_number ().uint ();
|
||||||
if (!util::is_integer (cmp))
|
if (!util::is_integer (cmp))
|
||||||
throw length_error ("minLength");
|
throw length_error ("minLength");
|
||||||
|
|
||||||
@ -206,31 +206,42 @@ validate (json::tree::string &node,
|
|||||||
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
template <typename T>
|
||||||
static void
|
static void
|
||||||
validate (json::tree::number &node,
|
validate_number (T val, const json::tree::object &schema) {
|
||||||
const json::tree::object &schema)
|
using R = json::tree::number::repr_t;
|
||||||
{
|
|
||||||
const auto &val = node.native ();
|
|
||||||
|
|
||||||
// check strictly positive integer multiple
|
// check strictly positive integer multiple
|
||||||
auto mult = schema.find ("multipleOf");
|
auto mult = schema.find ("multipleOf");
|
||||||
if (mult != schema.cend ()) {
|
if (mult != schema.cend ()) {
|
||||||
auto div = mult->second->as_number ().native ();
|
const auto &div = mult->second->as_number ();
|
||||||
|
|
||||||
if (val <= 0 || util::almost_equal (val, div))
|
switch (div.repr ()) {
|
||||||
throw json::schema_error ("multipleOf");
|
case R::REAL: if (util::exactly_zero (std::fmod (val, div.real ()))) throw json::schema_error ("multipleOf"); break;
|
||||||
|
case R::SINT: if (util::exactly_zero (std::fmod (val, div.sint ()))) throw json::schema_error ("multipleOf"); break;
|
||||||
|
case R::UINT: if (util::exactly_zero (std::fmod (val, div.uint ()))) throw json::schema_error ("multipleOf"); break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// check maximum holds. exclusive requires max condition.
|
// check maximum holds. exclusive requires max condition.
|
||||||
auto max = schema.find ("maximum");
|
auto max = schema.find ("maximum");
|
||||||
auto exclusiveMax = schema.find ("exclusiveMaximum");
|
auto exclusiveMax = schema.find ("exclusiveMaximum");
|
||||||
if (max != schema.end ()) {
|
if (max != schema.end ()) {
|
||||||
auto cmp = max->second->as_number ().native ();
|
const auto &cmp = max->second->as_number ();
|
||||||
|
|
||||||
if (exclusiveMax != schema.end () && exclusiveMax->second->as_boolean () && val >= cmp)
|
if (exclusiveMax != schema.end () && exclusiveMax->second->as_boolean ()) {
|
||||||
throw json::schema_error ("exclusiveMax");
|
switch (cmp.repr ()) {
|
||||||
else if (val > cmp)
|
case R::REAL: if (val >= T(cmp.real ())) throw json::schema_error ("exclusiveMax"); break;
|
||||||
throw json::schema_error ("maximum");
|
case R::SINT: if (val >= T(cmp.uint ())) throw json::schema_error ("exclusiveMax"); break;
|
||||||
|
case R::UINT: if (val >= T(cmp.sint ())) throw json::schema_error ("exclusiveMax"); break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch (cmp.repr ()) {
|
||||||
|
case R::REAL: if (val > T(cmp.real ())) throw json::schema_error ("maximum"); break;
|
||||||
|
case R::SINT: if (val > T(cmp.sint ())) throw json::schema_error ("maximum"); break;
|
||||||
|
case R::UINT: if (val > T(cmp.uint ())) throw json::schema_error ("maximum"); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (exclusiveMax != schema.cend ())
|
if (exclusiveMax != schema.cend ())
|
||||||
throw json::schema_error ("exclusiveMax");
|
throw json::schema_error ("exclusiveMax");
|
||||||
@ -240,16 +251,42 @@ validate (json::tree::number &node,
|
|||||||
auto min = schema.find ("minimum");
|
auto min = schema.find ("minimum");
|
||||||
auto exclusiveMin = schema.find ("exclusiveMinimum");
|
auto exclusiveMin = schema.find ("exclusiveMinimum");
|
||||||
if (min != schema.end ()) {
|
if (min != schema.end ()) {
|
||||||
auto cmp = min->second->as_number ().native ();
|
const auto &cmp = min->second->as_number ();
|
||||||
|
|
||||||
if (exclusiveMin != schema.end () && exclusiveMin->second->as_boolean () && val <= cmp)
|
if (exclusiveMin != schema.end () && exclusiveMin->second->as_boolean ()) {
|
||||||
throw json::schema_error ("exclusiveMin");
|
switch (cmp.repr ()) {
|
||||||
else if (val < cmp)
|
case R::REAL: if (val <= T(cmp.real ())) throw json::schema_error ("exclusiveMin"); break;
|
||||||
throw json::schema_error ("minimum");
|
case R::SINT: if (val <= T(cmp.sint ())) throw json::schema_error ("exclusiveMin"); break;
|
||||||
|
case R::UINT: if (val <= T(cmp.uint ())) throw json::schema_error ("exclusiveMin"); break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch (cmp.repr ()) {
|
||||||
|
case R::REAL: if (val < T(cmp.real ())) throw json::schema_error ("minimum"); break;
|
||||||
|
case R::SINT: if (val < T(cmp.sint ())) throw json::schema_error ("minimum"); break;
|
||||||
|
case R::UINT: if (val < T(cmp.uint ())) throw json::schema_error ("minimum"); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (exclusiveMin != schema.cend ())
|
if (exclusiveMin != schema.cend ())
|
||||||
throw json::schema_error ("exclusiveMin");
|
throw json::schema_error ("exclusiveMin");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static void
|
||||||
|
validate (json::tree::number &node,
|
||||||
|
const json::tree::object &schema)
|
||||||
|
{
|
||||||
|
using N = json::tree::number;
|
||||||
|
using R = N::repr_t;
|
||||||
|
|
||||||
|
switch (node.repr ()) {
|
||||||
|
case R::REAL: validate_number<N::real_t> (node.real (), schema); break;
|
||||||
|
case R::SINT: validate_number<N::sint_t> (node.sint (), schema); break;
|
||||||
|
case R::UINT: validate_number<N::uint_t> (node.uint (), schema); break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -304,7 +341,7 @@ validate (json::tree::node &node,
|
|||||||
if (type != schema.cend ()) {
|
if (type != schema.cend ()) {
|
||||||
// check against a single named type
|
// check against a single named type
|
||||||
if (type->second->is_string ()) {
|
if (type->second->is_string ()) {
|
||||||
auto a = type->second->as_string ();
|
const auto &a = type->second->as_string ();
|
||||||
auto b = to_string (node.type ());
|
auto b = to_string (node.type ());
|
||||||
|
|
||||||
if (a != b)
|
if (a != b)
|
||||||
|
243
json/tree.cpp
243
json/tree.cpp
@ -49,21 +49,29 @@ using json::tree::null;
|
|||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
namespace util {
|
namespace util {
|
||||||
template <>
|
|
||||||
bool
|
bool
|
||||||
is_integer (const json::tree::number &node)
|
is_integer (const json::tree::number &node)
|
||||||
{
|
{
|
||||||
return is_integer (node.native ());
|
using R = json::tree::number::repr_t;
|
||||||
|
|
||||||
|
switch (node.repr ()) {
|
||||||
|
case R::REAL:
|
||||||
|
return is_integer (node.real ());
|
||||||
|
|
||||||
|
case R::SINT:
|
||||||
|
case R::UINT:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
unreachable ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
template <>
|
|
||||||
bool
|
bool
|
||||||
is_integer (const json::tree::node &node)
|
is_integer (const json::tree::node &node)
|
||||||
{
|
{
|
||||||
return node.is_number () &&
|
return node.is_number () && is_integer (node.as_number ());
|
||||||
is_integer (node.as_number ());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,43 +135,67 @@ parse (std::vector<json::flat::item>::const_iterator first,
|
|||||||
CHECK (first != last);
|
CHECK (first != last);
|
||||||
CHECK (output.get () == nullptr);
|
CHECK (output.get () == nullptr);
|
||||||
|
|
||||||
|
using T = json::flat::type;
|
||||||
|
|
||||||
switch (first->tag) {
|
switch (first->tag) {
|
||||||
case json::flat::type::NUL:
|
case T::NUL:
|
||||||
output.reset (new json::tree::null ());
|
output.reset (new json::tree::null ());
|
||||||
return first + 1;
|
return first + 1;
|
||||||
|
|
||||||
case json::flat::type::BOOLEAN:
|
case T::BOOLEAN:
|
||||||
CHECK (*first->first == 't' || *first->first == 'f');
|
CHECK (*first->first == 't' || *first->first == 'f');
|
||||||
output.reset (new json::tree::boolean (*first->first == 't'));
|
output.reset (new json::tree::boolean (*first->first == 't'));
|
||||||
return first + 1;
|
return first + 1;
|
||||||
|
|
||||||
case json::flat::type::STRING:
|
case T::STRING:
|
||||||
CHECK_NEQ (first->first, first->last);
|
CHECK_NEQ (first->first, first->last);
|
||||||
output.reset (new json::tree::string (first->first + 1, first->last - 1));
|
output.reset (new json::tree::string (first->first + 1, first->last - 1));
|
||||||
return first + 1;
|
return first + 1;
|
||||||
|
|
||||||
case json::flat::type::INTEGER:
|
case T::INTEGER:
|
||||||
case json::flat::type::REAL:
|
if (first->first[0] == '-') {
|
||||||
|
char *end;
|
||||||
|
intmax_t v = strtoll (first->first, &end, 10);
|
||||||
|
|
||||||
|
if (end == first->first || end > first->last)
|
||||||
|
throw json::parse_error ("invalid signed integer");
|
||||||
|
output.reset (new json::tree::number (v));
|
||||||
|
} else {
|
||||||
|
char *end;
|
||||||
|
uintmax_t v = strtoull (first->first, &end, 10);
|
||||||
|
|
||||||
|
if (end == first->first || end > first->last)
|
||||||
|
throw json::parse_error ("invalid unsigned integer");
|
||||||
|
output.reset (new json::tree::number (v));
|
||||||
|
}
|
||||||
|
|
||||||
|
return first + 1;
|
||||||
|
|
||||||
|
case T::REAL:
|
||||||
output.reset (new json::tree::number (std::atof (first->first)));
|
output.reset (new json::tree::number (std::atof (first->first)));
|
||||||
return first + 1;
|
return first + 1;
|
||||||
|
|
||||||
case json::flat::type::ARRAY_BEGIN: {
|
case T::ARRAY_BEGIN: {
|
||||||
auto value = std::make_unique<json::tree::array> ();
|
auto value = std::make_unique<json::tree::array> ();
|
||||||
auto cursor = ::parse (first + 1, last, *value);
|
auto cursor = ::parse (first + 1, last, *value);
|
||||||
output = std::move (value);
|
output = std::move (value);
|
||||||
return cursor;
|
return cursor;
|
||||||
}
|
}
|
||||||
|
|
||||||
case json::flat::type::OBJECT_BEGIN: {
|
case T::OBJECT_BEGIN: {
|
||||||
auto value = std::make_unique<json::tree::object> ();
|
auto value = std::make_unique<json::tree::object> ();
|
||||||
auto cursor = ::parse (first + 1, last, *value);
|
auto cursor = ::parse (first + 1, last, *value);
|
||||||
output = std::move (value);
|
output = std::move (value);
|
||||||
return cursor;
|
return cursor;
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
case T::UNKNOWN:
|
||||||
|
case T::OBJECT_END:
|
||||||
|
case T::ARRAY_END:
|
||||||
unreachable ();
|
unreachable ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unreachable ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -304,7 +336,7 @@ json::tree::node::as_bool (void) const
|
|||||||
float
|
float
|
||||||
json::tree::node::as_float (void) const
|
json::tree::node::as_float (void) const
|
||||||
{
|
{
|
||||||
return static_cast<float> (as_number ().native ());
|
return static_cast<float> (as_number ().real ());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -312,20 +344,23 @@ json::tree::node::as_float (void) const
|
|||||||
double
|
double
|
||||||
json::tree::node::as_double (void) const
|
json::tree::node::as_double (void) const
|
||||||
{
|
{
|
||||||
return as_number ().native ();
|
return as_number ().real ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
size_t
|
uintmax_t
|
||||||
json::tree::node::as_uint (void) const
|
json::tree::node::as_uint (void) const
|
||||||
{
|
{
|
||||||
auto val = as_number ().native ();
|
return as_number ().uint ();
|
||||||
if (!util::is_integer (val))
|
}
|
||||||
throw json::type_error ("cast fractional value to uint");
|
|
||||||
|
|
||||||
// TODO: use trunc_cast
|
|
||||||
return static_cast<size_t> (val);
|
//-----------------------------------------------------------------------------
|
||||||
|
intmax_t
|
||||||
|
json::tree::node::as_sint (void) const
|
||||||
|
{
|
||||||
|
return as_number ().sint ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -344,45 +379,60 @@ namespace json { namespace tree {
|
|||||||
{
|
{
|
||||||
return as_bool ();
|
return as_bool ();
|
||||||
}
|
}
|
||||||
} }
|
|
||||||
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
namespace json { namespace tree {
|
|
||||||
template <>
|
template <>
|
||||||
float json::tree::node::as (void) const
|
float json::tree::node::as (void) const
|
||||||
{
|
{
|
||||||
return as_float ();
|
return as_float ();
|
||||||
}
|
}
|
||||||
} }
|
|
||||||
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
namespace json { namespace tree {
|
|
||||||
template <>
|
template <>
|
||||||
double
|
double
|
||||||
json::tree::node::as (void) const
|
json::tree::node::as (void) const
|
||||||
{
|
{
|
||||||
return as_double ();
|
return as_double ();
|
||||||
}
|
}
|
||||||
} }
|
|
||||||
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
#define AS_INTEGRAL(T) \
|
template <>
|
||||||
namespace json { namespace tree { \
|
uint32_t
|
||||||
template <> \
|
json::tree::node::as (void) const
|
||||||
T \
|
{
|
||||||
node::as (void) const \
|
return static_cast<uint32_t> (as_uint ());
|
||||||
{ \
|
}
|
||||||
return static_cast<T> (as_double ()); \
|
|
||||||
} \
|
|
||||||
} }
|
|
||||||
|
|
||||||
AS_INTEGRAL(uint8_t)
|
|
||||||
AS_INTEGRAL(uint16_t)
|
//-----------------------------------------------------------------------------
|
||||||
AS_INTEGRAL(uint32_t)
|
template <>
|
||||||
AS_INTEGRAL(uint64_t)
|
uint64_t
|
||||||
|
json::tree::node::as (void) const
|
||||||
|
{
|
||||||
|
return static_cast<uint64_t> (as_uint ());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
template <>
|
||||||
|
int32_t
|
||||||
|
json::tree::node::as (void) const
|
||||||
|
{
|
||||||
|
return static_cast<int32_t> (as_sint ());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
template <>
|
||||||
|
int64_t
|
||||||
|
json::tree::node::as (void) const
|
||||||
|
{
|
||||||
|
return static_cast<int64_t> (as_sint ());
|
||||||
|
}
|
||||||
|
} }
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
@ -411,7 +461,7 @@ json::tree::node::operator[] (const std::string &key)&
|
|||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
json::tree::node&
|
json::tree::node&
|
||||||
json::tree::node::operator[] (unsigned int idx)&
|
json::tree::node::operator[] (size_t idx)&
|
||||||
{ return as_array()[idx]; }
|
{ return as_array()[idx]; }
|
||||||
|
|
||||||
|
|
||||||
@ -423,7 +473,7 @@ json::tree::node::operator[] (const std::string &key) const&
|
|||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
const json::tree::node&
|
const json::tree::node&
|
||||||
json::tree::node::operator[] (unsigned int idx) const&
|
json::tree::node::operator[] (size_t idx) const&
|
||||||
{ return as_array()[idx]; }
|
{ return as_array()[idx]; }
|
||||||
|
|
||||||
|
|
||||||
@ -634,7 +684,7 @@ json::tree::array::size (void) const
|
|||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
json::tree::node&
|
json::tree::node&
|
||||||
json::tree::array::operator[] (unsigned int idx)&
|
json::tree::array::operator[] (size_t idx)&
|
||||||
{
|
{
|
||||||
return *m_values[idx];
|
return *m_values[idx];
|
||||||
}
|
}
|
||||||
@ -642,7 +692,7 @@ json::tree::array::operator[] (unsigned int idx)&
|
|||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
const json::tree::node&
|
const json::tree::node&
|
||||||
json::tree::array::operator[] (unsigned int idx) const&
|
json::tree::array::operator[] (size_t idx) const&
|
||||||
{
|
{
|
||||||
return *m_values[idx];
|
return *m_values[idx];
|
||||||
}
|
}
|
||||||
@ -761,7 +811,13 @@ json::tree::string::operator== (const std::string &rhs) const
|
|||||||
std::unique_ptr<json::tree::node>
|
std::unique_ptr<json::tree::node>
|
||||||
json::tree::number::clone (void) const
|
json::tree::number::clone (void) const
|
||||||
{
|
{
|
||||||
return std::make_unique<json::tree::number> (m_value);
|
switch (m_repr) {
|
||||||
|
case REAL: return std::make_unique<json::tree::number> (m_value.r);
|
||||||
|
case SINT: return std::make_unique<json::tree::number> (m_value.s);
|
||||||
|
case UINT: return std::make_unique<json::tree::number> (m_value.u);
|
||||||
|
}
|
||||||
|
|
||||||
|
unreachable ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -769,15 +825,86 @@ json::tree::number::clone (void) const
|
|||||||
std::ostream&
|
std::ostream&
|
||||||
json::tree::number::write (std::ostream &os) const
|
json::tree::number::write (std::ostream &os) const
|
||||||
{
|
{
|
||||||
os << std::setprecision (std::numeric_limits<double>::digits10) << m_value;
|
auto old = int (os.precision ());
|
||||||
return os;
|
|
||||||
|
switch (m_repr) {
|
||||||
|
case REAL: return os << std::numeric_limits<real_t>::digits10 << m_value.r << std::setprecision (old);
|
||||||
|
case SINT: return os << std::numeric_limits<sint_t>::digits10 << m_value.s << std::setprecision (old);
|
||||||
|
case UINT: return os << std::numeric_limits<uint_t>::digits10 << m_value.u << std::setprecision (old);
|
||||||
|
}
|
||||||
|
|
||||||
|
unreachable ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
bool
|
bool
|
||||||
json::tree::number::operator ==(const json::tree::number &rhs) const
|
json::tree::number::operator ==(const json::tree::number &rhs) const {
|
||||||
{ return util::almost_equal (rhs.m_value, m_value); }
|
if (repr () != rhs.repr ())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
switch (repr ()) {
|
||||||
|
case REAL: return util::almost_equal (real (), rhs.real ());
|
||||||
|
case SINT: return util::almost_equal (sint (), rhs.sint ());
|
||||||
|
case UINT: return util::almost_equal (uint (), rhs.uint ());
|
||||||
|
}
|
||||||
|
|
||||||
|
unreachable ();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
json::tree::number::real_t
|
||||||
|
json::tree::number::real (void) const
|
||||||
|
{
|
||||||
|
if (m_repr != REAL)
|
||||||
|
throw json::type_error ("number is not a real");
|
||||||
|
|
||||||
|
return m_value.r;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
json::tree::number::sint_t
|
||||||
|
json::tree::number::sint (void) const
|
||||||
|
{
|
||||||
|
if (m_repr != SINT)
|
||||||
|
throw json::type_error ("number is not a sint");
|
||||||
|
|
||||||
|
return m_value.s;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
json::tree::number::uint_t
|
||||||
|
json::tree::number::uint (void) const
|
||||||
|
{
|
||||||
|
if (m_repr != UINT)
|
||||||
|
throw json::type_error ("number is not a uint");
|
||||||
|
|
||||||
|
return m_value.u;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
json::tree::number::operator json::tree::number::real_t (void) const
|
||||||
|
{
|
||||||
|
return real ();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
json::tree::number::operator json::tree::number::sint_t (void) const
|
||||||
|
{
|
||||||
|
return sint ();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
json::tree::number::operator json::tree::number::uint_t (void) const
|
||||||
|
{
|
||||||
|
return uint ();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
@ -854,14 +981,26 @@ namespace json { namespace tree {
|
|||||||
|
|
||||||
template <>
|
template <>
|
||||||
std::unique_ptr<node>
|
std::unique_ptr<node>
|
||||||
io<int>::serialise (const int &i) {
|
io<int32_t>::serialise (const int32_t &i) {
|
||||||
return std::unique_ptr<node> (new number (i));
|
return std::unique_ptr<node> (new number (intmax_t {i}));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
std::unique_ptr<node>
|
std::unique_ptr<node>
|
||||||
io<size_t>::serialise (const size_t &i) {
|
io<int64_t>::serialise (const int64_t &i) {
|
||||||
return std::unique_ptr<node> (new number (i));
|
return std::unique_ptr<node> (new number (intmax_t {i}));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
std::unique_ptr<node>
|
||||||
|
io<uint32_t>::serialise (const uint32_t &i) {
|
||||||
|
return std::unique_ptr<node> (new number (uintmax_t {i}));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
std::unique_ptr<node>
|
||||||
|
io<uint64_t>::serialise (const uint64_t &i) {
|
||||||
|
return std::unique_ptr<node> (new number (uintmax_t {i}));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
|
@ -52,6 +52,7 @@ namespace json { namespace tree {
|
|||||||
/// Abstract base for all JSON values
|
/// Abstract base for all JSON values
|
||||||
class node {
|
class node {
|
||||||
public:
|
public:
|
||||||
|
node (const node&) = delete;
|
||||||
virtual ~node () { ; }
|
virtual ~node () { ; }
|
||||||
virtual std::unique_ptr<node> clone (void) const = 0;
|
virtual std::unique_ptr<node> clone (void) const = 0;
|
||||||
|
|
||||||
@ -75,7 +76,8 @@ namespace json { namespace tree {
|
|||||||
virtual bool as_bool (void) const;
|
virtual bool as_bool (void) const;
|
||||||
virtual float as_float (void) const;
|
virtual float as_float (void) const;
|
||||||
virtual double as_double (void) const;
|
virtual double as_double (void) const;
|
||||||
virtual size_t as_uint (void) const;
|
virtual intmax_t as_sint (void) const;
|
||||||
|
virtual uintmax_t as_uint (void) const;
|
||||||
virtual const char* as_chars (void) const&;
|
virtual const char* as_chars (void) const&;
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@ -103,24 +105,26 @@ namespace json { namespace tree {
|
|||||||
virtual bool operator!=(const char *rhs) const { return !(*this == rhs); }
|
virtual bool operator!=(const char *rhs) const { return !(*this == rhs); }
|
||||||
|
|
||||||
virtual node& operator[] (const std::string&)&;
|
virtual node& operator[] (const std::string&)&;
|
||||||
virtual node& operator[] (unsigned int)&;
|
virtual node& operator[] (size_t)&;
|
||||||
virtual const node& operator[] (const std::string&) const&;
|
virtual const node& operator[] (const std::string&) const&;
|
||||||
virtual const node& operator[] (unsigned int) const&;
|
virtual const node& operator[] (size_t) const&;
|
||||||
|
|
||||||
virtual std::ostream& write (std::ostream &os) const = 0;
|
virtual std::ostream& write (std::ostream &os) const = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
node () = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/// Represents a JSON object, and contains its children.
|
/// Represents a JSON object, and contains its children.
|
||||||
class object final : public node {
|
class object final : public node {
|
||||||
protected:
|
private:
|
||||||
typedef std::map<std::string, std::unique_ptr<node>> value_store;
|
using value_store = std::map<std::string, std::unique_ptr<node>>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
typedef value_store::iterator iterator;
|
typedef value_store::iterator iterator;
|
||||||
typedef value_store::const_iterator const_iterator;
|
typedef value_store::const_iterator const_iterator;
|
||||||
|
|
||||||
protected:
|
|
||||||
value_store m_values;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual ~object ();
|
virtual ~object ();
|
||||||
@ -136,8 +140,8 @@ namespace json { namespace tree {
|
|||||||
{ return rhs == *this; }
|
{ return rhs == *this; }
|
||||||
|
|
||||||
virtual void insert (const std::string &key, std::unique_ptr<node>&& value);
|
virtual void insert (const std::string &key, std::unique_ptr<node>&& value);
|
||||||
virtual const node& operator[](const std::string &key) const& override;
|
virtual const node& operator[] (const std::string &key) const& override;
|
||||||
virtual node& operator[](const std::string &key)& override;
|
virtual node& operator[] (const std::string &key)& override;
|
||||||
virtual bool has (const std::string&) const;
|
virtual bool has (const std::string&) const;
|
||||||
|
|
||||||
virtual const_iterator find (const std::string&) const;
|
virtual const_iterator find (const std::string&) const;
|
||||||
@ -153,6 +157,9 @@ namespace json { namespace tree {
|
|||||||
virtual void erase (const std::string &key);
|
virtual void erase (const std::string &key);
|
||||||
|
|
||||||
virtual std::ostream& write (std::ostream &os) const override;
|
virtual std::ostream& write (std::ostream &os) const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
value_store m_values;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -182,8 +189,8 @@ namespace json { namespace tree {
|
|||||||
virtual bool operator==(const node &rhs) const override;
|
virtual bool operator==(const node &rhs) const override;
|
||||||
|
|
||||||
virtual size_t size (void) const;
|
virtual size_t size (void) const;
|
||||||
virtual node& operator [](unsigned int idx)& override;
|
virtual node& operator[] (size_t idx)& override;
|
||||||
virtual const node& operator [](unsigned int idx) const& override;
|
virtual const node& operator[] (size_t idx) const& override;
|
||||||
|
|
||||||
virtual iterator begin (void);
|
virtual iterator begin (void);
|
||||||
virtual iterator end (void);
|
virtual iterator end (void);
|
||||||
@ -234,13 +241,21 @@ namespace json { namespace tree {
|
|||||||
|
|
||||||
/// Represents a JSON integer/float literal.
|
/// Represents a JSON integer/float literal.
|
||||||
class number final : public node {
|
class number final : public node {
|
||||||
protected:
|
|
||||||
double m_value;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit number (double _value): m_value (_value) { ; }
|
enum repr_t {
|
||||||
explicit number (int _value): m_value (_value) { ; }
|
REAL,
|
||||||
explicit number (size_t _value): m_value (_value) { ; }
|
SINT,
|
||||||
|
UINT
|
||||||
|
};
|
||||||
|
|
||||||
|
using real_t = double;
|
||||||
|
using sint_t = intmax_t;
|
||||||
|
using uint_t = uintmax_t;
|
||||||
|
|
||||||
|
|
||||||
|
explicit number (real_t _value): m_repr (REAL) { m_value.r = _value; }
|
||||||
|
explicit number (sint_t _value): m_repr (SINT) { m_value.s = _value; }
|
||||||
|
explicit number (uint_t _value): m_repr (UINT) { m_value.u = _value; }
|
||||||
virtual std::unique_ptr<node> clone (void) const override;
|
virtual std::unique_ptr<node> clone (void) const override;
|
||||||
|
|
||||||
virtual const number& as_number (void) const& override { return *this; }
|
virtual const number& as_number (void) const& override { return *this; }
|
||||||
@ -248,15 +263,30 @@ namespace json { namespace tree {
|
|||||||
virtual bool is_number (void) const override { return true; }
|
virtual bool is_number (void) const override { return true; }
|
||||||
|
|
||||||
virtual type_t type (void) const override { return NUMBER; }
|
virtual type_t type (void) const override { return NUMBER; }
|
||||||
|
virtual repr_t repr (void) const { return m_repr; }
|
||||||
|
|
||||||
virtual bool operator==(const number &rhs) const override;
|
virtual bool operator==(const number &rhs) const override;
|
||||||
virtual bool operator==(const node &rhs) const override
|
virtual bool operator==(const node &rhs) const override
|
||||||
{ return rhs == *this; }
|
{ return rhs == *this; }
|
||||||
|
|
||||||
operator double(void) const { return m_value; }
|
operator real_t (void) const;
|
||||||
double native (void) const { return m_value; }
|
operator sint_t (void) const;
|
||||||
|
operator uint_t (void) const;
|
||||||
|
|
||||||
|
real_t real (void) const;
|
||||||
|
sint_t sint (void) const;
|
||||||
|
uint_t uint (void) const;
|
||||||
|
|
||||||
virtual std::ostream& write (std::ostream &os) const override;
|
virtual std::ostream& write (std::ostream &os) const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
union {
|
||||||
|
real_t r;
|
||||||
|
sint_t s;
|
||||||
|
uint_t u;
|
||||||
|
} m_value;
|
||||||
|
|
||||||
|
repr_t m_repr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -54,7 +54,7 @@ main (void)
|
|||||||
tap.expect (!ref["integer"].is_string (), "integer not is_string");
|
tap.expect (!ref["integer"].is_string (), "integer not is_string");
|
||||||
tap.expect (
|
tap.expect (
|
||||||
util::exactly_equal (
|
util::exactly_equal (
|
||||||
(unsigned)ref["integer"].as_number ().native (),
|
(unsigned)ref["integer"].as_number ().as_uint (),
|
||||||
1u
|
1u
|
||||||
),
|
),
|
||||||
"integer value equality"
|
"integer value equality"
|
||||||
@ -102,7 +102,7 @@ main (void)
|
|||||||
tap.expect (!ref["double"].is_string (), "double not is_string");
|
tap.expect (!ref["double"].is_string (), "double not is_string");
|
||||||
tap.expect (
|
tap.expect (
|
||||||
util::exactly_equal (
|
util::exactly_equal (
|
||||||
ref["double"].as_number ().native (),
|
ref["double"].as_number ().as<double> (),
|
||||||
3.14
|
3.14
|
||||||
),
|
),
|
||||||
"double value equality"
|
"double value equality"
|
||||||
|
Loading…
Reference in New Issue
Block a user