From d9713fe8b703b9c3412ca78aa94f6f13191ab2ef Mon Sep 17 00:00:00 2001 From: Danny Robson Date: Tue, 20 Sep 2016 16:02:08 +1000 Subject: [PATCH] json/tree: fix signed min, unsigned max constraints signed/unsigned casting issues prevented correct comparisons with the min/max constraints. account for the source type's range before doing the comparison. --- json/schema.cpp | 89 +++++++++++++++++--- json/tree.cpp | 58 ++++++++++--- test/json/schema/signed_min.schema | 1 + test/json/schema/signed_min_0001_pass.json | 1 + test/json/schema/signed_min_0002_fail.json | 1 + test/json/schema/unsigned_max.schema | 1 + test/json/schema/unsigned_max_0001_pass.json | 1 + test/json/schema/unsigned_max_0002_fail.json | 1 + 8 files changed, 130 insertions(+), 23 deletions(-) create mode 100644 test/json/schema/signed_min.schema create mode 100644 test/json/schema/signed_min_0001_pass.json create mode 100644 test/json/schema/signed_min_0002_fail.json create mode 100644 test/json/schema/unsigned_max.schema create mode 100644 test/json/schema/unsigned_max_0001_pass.json create mode 100644 test/json/schema/unsigned_max_0002_fail.json diff --git a/json/schema.cpp b/json/schema.cpp index 9efda5d6..6821d5b3 100644 --- a/json/schema.cpp +++ b/json/schema.cpp @@ -32,6 +32,7 @@ struct length_error : public json::schema_error { }; +//----------------------------------------------------------------------------- struct format_error : public json::schema_error { using schema_error::schema_error; }; @@ -233,15 +234,47 @@ validate_number (T val, const json::tree::object &schema) { if (exclusiveMax != schema.end () && exclusiveMax->second->as_boolean ()) { switch (cmp.repr ()) { - case R::REAL: if (val >= T(cmp.real ())) throw json::schema_error ("exclusiveMax"); break; - 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; + case R::REAL: + if (T(val) >= cmp.real ()) + throw json::schema_error ("exclusiveMax"); + break; + + case R::SINT: + if (json::tree::number::sint_t(std::numeric_limits::max ()) >= cmp.sint () && + val >= T(cmp.sint ())) + { + throw json::schema_error ("exclusiveMax"); + } + break; + + case R::UINT: + if (json::tree::number::uint_t(std::numeric_limits::max ()) >= cmp.uint () && + val >= T(cmp.uint ())) + { + 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; + case R::REAL: + if (T(val) > cmp.real ()) + throw json::schema_error ("maximum"); + break; + case R::SINT: + if (json::tree::number::sint_t(std::numeric_limits::max ()) >= cmp.sint () && + val >= T(cmp.sint ())) + { + throw json::schema_error ("maximum"); + } + break; + case R::UINT: + if (json::tree::number::uint_t(std::numeric_limits::max ()) >= cmp.uint () && + val >= T(cmp.uint ())) + { + throw json::schema_error ("maximum"); + } + break; } } } else { @@ -257,15 +290,47 @@ validate_number (T val, const json::tree::object &schema) { if (exclusiveMin != schema.end () && exclusiveMin->second->as_boolean ()) { switch (cmp.repr ()) { - case R::REAL: if (val <= T(cmp.real ())) throw json::schema_error ("exclusiveMin"); break; - 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; + case R::REAL: + if (T(val) < cmp.real ()) + throw json::schema_error ("exclusiveMin"); + break; + case R::SINT: + if (cmp.sint () > json::tree::number::sint_t(std::numeric_limits::min ()) && + val < T(cmp.sint ())) + { + throw json::schema_error ("exclusiveMin"); + } + break; + case R::UINT: + if (cmp.uint () > json::tree::number::uint_t(std::numeric_limits::min ()) && + 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; + case R::REAL: + if (T(val) <= cmp.real ()) + throw json::schema_error ("minimum"); + break; + + case R::SINT: + if (cmp.sint () >= json::tree::number::sint_t(std::numeric_limits::min ()) && + val <= T(cmp.sint ())) + { + throw json::schema_error ("minimum"); + } + break; + + case R::UINT: + if (cmp.uint () >= json::tree::number::uint_t(std::numeric_limits::min ()) && + val <= T(cmp.uint ())) + { + throw json::schema_error ("minimum"); + } + break; } } } else { diff --git a/json/tree.cpp b/json/tree.cpp index f7aae124..7e94a44f 100644 --- a/json/tree.cpp +++ b/json/tree.cpp @@ -869,14 +869,26 @@ json::tree::number::operator ==(const json::tree::number &rhs) const { } -//----------------------------------------------------------------------------- +/////////////////////////////////////////////////////////////////////////////// json::tree::number::real_t json::tree::number::real (void) const { - if (m_repr != REAL) - throw json::type_error ("number is not a real"); + switch (repr ()) { + case REAL: + return m_value.r; - return m_value.r; + case UINT: + if (uint_t (real_t (m_value.u)) != m_value.u) + throw type_error ("number is not a real"); + return m_value.u; + + case SINT: + if (sint_t (real_t (m_value.s)) != m_value.s) + throw type_error ("number is not a real"); + return m_value.s; + } + + unreachable (); } @@ -884,10 +896,22 @@ json::tree::number::real (void) const json::tree::number::sint_t json::tree::number::sint (void) const { - if (m_repr != SINT) - throw json::type_error ("number is not a sint"); + switch (repr ()) { + case SINT: + return m_value.s; - return m_value.s; + case REAL: + if (!::util::exactly_equal (real_t (sint_t (m_value.r)), m_value.r)) + throw type_error ("number is not a sint"); + return sint_t (m_value.r); + + case UINT: + if (uint_t (sint_t (m_value.u)) != m_value.u) + throw type_error ("number is not a sint"); + return m_value.s; + } + + unreachable (); } @@ -895,14 +919,26 @@ json::tree::number::sint (void) const json::tree::number::uint_t json::tree::number::uint (void) const { - if (m_repr != UINT) - throw json::type_error ("number is not a uint"); + switch (repr ()) { + case UINT: + return m_value.u; - return m_value.u; + case REAL: + if (!::util::exactly_equal (real_t (uint_t (m_value.r)), m_value.r)) + throw type_error ("number is not a uint"); + return uint_t (m_value.r); + + case SINT: + if (sint_t (uint_t (m_value.s)) != m_value.s) + throw type_error ("number is not a uint"); + return m_value.s; + } + + unreachable (); } -//----------------------------------------------------------------------------- +/////////////////////////////////////////////////////////////////////////////// json::tree::number::operator json::tree::number::real_t (void) const { return real (); diff --git a/test/json/schema/signed_min.schema b/test/json/schema/signed_min.schema new file mode 100644 index 00000000..33e581a6 --- /dev/null +++ b/test/json/schema/signed_min.schema @@ -0,0 +1 @@ +{ "type": "number", "minimum": -1 } diff --git a/test/json/schema/signed_min_0001_pass.json b/test/json/schema/signed_min_0001_pass.json new file mode 100644 index 00000000..573541ac --- /dev/null +++ b/test/json/schema/signed_min_0001_pass.json @@ -0,0 +1 @@ +0 diff --git a/test/json/schema/signed_min_0002_fail.json b/test/json/schema/signed_min_0002_fail.json new file mode 100644 index 00000000..3a2e3f49 --- /dev/null +++ b/test/json/schema/signed_min_0002_fail.json @@ -0,0 +1 @@ +-1 diff --git a/test/json/schema/unsigned_max.schema b/test/json/schema/unsigned_max.schema new file mode 100644 index 00000000..bdae19e7 --- /dev/null +++ b/test/json/schema/unsigned_max.schema @@ -0,0 +1 @@ +{ "type": "number", "maximum": 1 } diff --git a/test/json/schema/unsigned_max_0001_pass.json b/test/json/schema/unsigned_max_0001_pass.json new file mode 100644 index 00000000..3a2e3f49 --- /dev/null +++ b/test/json/schema/unsigned_max_0001_pass.json @@ -0,0 +1 @@ +-1 diff --git a/test/json/schema/unsigned_max_0002_fail.json b/test/json/schema/unsigned_max_0002_fail.json new file mode 100644 index 00000000..d00491fd --- /dev/null +++ b/test/json/schema/unsigned_max_0002_fail.json @@ -0,0 +1 @@ +1