diff --git a/src/db/unit_tests/dbTransTests.cc b/src/db/unit_tests/dbTransTests.cc index 5fd16a030..79ae3efd9 100644 --- a/src/db/unit_tests/dbTransTests.cc +++ b/src/db/unit_tests/dbTransTests.cc @@ -420,4 +420,25 @@ TEST(15) EXPECT_EQ (db::Trans (t).to_string (), "r0 0,0"); } +TEST(16_issue2362) +{ + db::Trans t, tt; + std::string ts; + tl::Extractor ex; + + t = db::Trans (0, false, db::Vector (std::numeric_limits::min (), std::numeric_limits::min ())); + ts = t.to_string (); + + ex = tl::Extractor (ts.c_str ()); + ex.read (tt); + + EXPECT_EQ (ts, tt.to_string ()); + t = db::Trans (0, false, db::Vector (std::numeric_limits::max (), std::numeric_limits::max ())); + ts = t.to_string (); + + ex = tl::Extractor (ts.c_str ()); + ex.read (tt); + + EXPECT_EQ (ts, tt.to_string ()); +} diff --git a/src/pya/pya/pyaConvert.h b/src/pya/pya/pyaConvert.h index 5969093e2..559146750 100644 --- a/src/pya/pya/pyaConvert.h +++ b/src/pya/pya/pyaConvert.h @@ -32,9 +32,11 @@ #include "tlVariant.h" #include "tlException.h" +#include "tlInternational.h" #include "tlHeap.h" #include +#include #if defined(HAVE_QT) # include # include @@ -316,12 +318,28 @@ struct python2c_func_cast } }; +template +struct python2c_func_cast_check + : public python2c_func +{ + D operator() (PyObject *rval) + { + C c = python2c_func::operator() (rval); + if (c < std::numeric_limits::min ()) { + throw tl::TypeError (tl::to_string (tr ("Value out of range: ")) + tl::to_string (c) + tl::to_string (tr (", min value is ")) + tl::to_string ((C) std::numeric_limits::min ())); + } else if (c > std::numeric_limits::max ()) { + throw tl::TypeError (tl::to_string (tr ("Value out of range: ")) + tl::to_string (c) + tl::to_string (tr (", max value is ")) + tl::to_string ((C) std::numeric_limits::max ())); + } + return (D)c; + } +}; + template <> struct python2c_func : public python2c_func_cast { }; template <> struct python2c_func : public python2c_func_cast { }; -template <> struct python2c_func : public python2c_func_cast { }; -template <> struct python2c_func : public python2c_func_cast { }; -template <> struct python2c_func : public python2c_func_cast { }; -template <> struct python2c_func : public python2c_func_cast { }; +template <> struct python2c_func : public python2c_func_cast_check { }; +template <> struct python2c_func : public python2c_func_cast_check { }; +template <> struct python2c_func : public python2c_func_cast_check { }; +template <> struct python2c_func : public python2c_func_cast_check { }; template <> PYA_PUBLIC long long python2c_func::operator() (PyObject *rval); template <> PYA_PUBLIC unsigned long long python2c_func::operator() (PyObject *rval); diff --git a/src/tl/tl/tlString.cc b/src/tl/tl/tlString.cc index 9242d99c7..2f4cb8ed1 100644 --- a/src/tl/tl/tlString.cc +++ b/src/tl/tl/tlString.cc @@ -1268,20 +1268,36 @@ Extractor::try_read_signed_int (T &value) } value = 0; - while (safe_isdigit (*m_cp)) { - if (value > std::numeric_limits::max () / 10) { - throw tl::Exception (overflow_msg_func () ()); + + if (minus) { + + // NOTE: that's a separate path as there are more numbers in the negative case + while (safe_isdigit (*m_cp)) { + if (value < std::numeric_limits::min () / 10) { + throw tl::Exception (overflow_msg_func () ()); + } + value *= 10; + if (value < std::numeric_limits::min () + (*m_cp - '0')) { + throw tl::Exception (overflow_msg_func () ()); + } + value -= (*m_cp - '0'); + ++m_cp; } - value *= 10; - if (value > std::numeric_limits::max () - (*m_cp - '0')) { - throw tl::Exception (overflow_msg_func () ()); + + } else { + + while (safe_isdigit (*m_cp)) { + if (value > std::numeric_limits::max () / 10) { + throw tl::Exception (overflow_msg_func () ()); + } + value *= 10; + if (value > std::numeric_limits::max () - (*m_cp - '0')) { + throw tl::Exception (overflow_msg_func () ()); + } + value += (*m_cp - '0'); + ++m_cp; } - value += (*m_cp - '0'); - ++m_cp; - } - if (minus) { - value = -value; } return true; diff --git a/src/tl/unit_tests/tlStringTests.cc b/src/tl/unit_tests/tlStringTests.cc index 28ded35d8..db43169c2 100644 --- a/src/tl/unit_tests/tlStringTests.cc +++ b/src/tl/unit_tests/tlStringTests.cc @@ -126,6 +126,14 @@ TEST(1a) ex.expect ("x"); } +template +void +ex_from_string (const std::string &s, T &v) +{ + tl::Extractor ex (s.c_str ()); + ex.read (v); +} + TEST(2) { long l; @@ -663,3 +671,56 @@ TEST(16) EXPECT_EQ (ex.try_read (x), true); EXPECT_EQ (tl::to_string (x), "-inf"); } + +// border cases of int values: round trip value->string->value +TEST(17) +{ + int n, nn; + n = std::numeric_limits::max (); + ex_from_string (to_string (n), nn); + EXPECT_EQ (n, nn); + n = std::numeric_limits::min (); + ex_from_string (to_string (n), nn); + EXPECT_EQ (n, nn); + + unsigned int un, unn; + un = std::numeric_limits::max (); + ex_from_string (to_string (un), unn); + EXPECT_EQ (un, unn); + un = std::numeric_limits::min (); + ex_from_string (to_string (un), unn); + EXPECT_EQ (un, unn); + + long ln, lnn; + ln = std::numeric_limits::max (); + ex_from_string (to_string (ln), lnn); + EXPECT_EQ (ln, lnn); + ln = std::numeric_limits::min (); + ex_from_string (to_string (ln), lnn); + EXPECT_EQ (ln, lnn); + + unsigned long uln, ulnn; + uln = std::numeric_limits::max (); + ex_from_string (to_string (uln), ulnn); + EXPECT_EQ (uln, ulnn); + uln = std::numeric_limits::min (); + ex_from_string (to_string (uln), ulnn); + EXPECT_EQ (uln, ulnn); + + long long lln, llnn; + lln = std::numeric_limits::max (); + ex_from_string (to_string (lln), llnn); + EXPECT_EQ (lln, llnn); + lln = std::numeric_limits::min (); + ex_from_string (to_string (lln), llnn); + EXPECT_EQ (lln, llnn); + + unsigned long long ulln, ullnn; + ulln = std::numeric_limits::max (); + ex_from_string (to_string (ulln), ullnn); + EXPECT_EQ (ulln, ullnn); + ulln = std::numeric_limits::min (); + ex_from_string (to_string (ulln), ullnn); + EXPECT_EQ (ulln, ullnn); +} + diff --git a/testdata/python/basic.py b/testdata/python/basic.py index bda4dff14..fc988880b 100644 --- a/testdata/python/basic.py +++ b/testdata/python/basic.py @@ -537,20 +537,20 @@ def test_12(self): self.assertEqual( a3.get_n(), -11 ) self.assertEqual( a1.a10_d(5.2), "5.2" ) - self.assertEqual( a1.a10_s(0x70000000), "0" ) - self.assertEqual( a1.a10_s(0x7fffffff), "-1" ) - self.assertEqual( a1.a10_us(0x70000000), "0" ) - self.assertEqual( a1.a10_us(0x7fffffff), "65535" ) + self.assertEqual( a1.a10_s(0x7fff), "32767" ) + self.assertEqual( a1.a10_s(-32768), "-32768" ) + self.assertEqual( a1.a10_us(0), "0" ) + self.assertEqual( a1.a10_us(0xffff), "65535" ) self.assertEqual( a1.a10_i(-0x80000000), "-2147483648" ) self.assertEqual( a1.a10_l(-0x80000000), "-2147483648" ) self.assertEqual( a1.a10_ll(-0x80000000), "-2147483648" ) self.assertEqual( a1.a10_ui(0xffffffff), "4294967295" ) self.assertEqual( a1.a10_ul(0xffffffff), "4294967295" ) self.assertEqual( a1.a10_ull(0xffffffff), "4294967295" ) - self.assertEqual( a1.a11_s(0x70000000), 0 ) - self.assertEqual( a1.a11_s(0x7fffffff), -1 ) - self.assertEqual( a1.a11_us(0x70000000), 0 ) - self.assertEqual( a1.a11_us(0x7fffffff), 65535 ) + self.assertEqual( a1.a11_s(0x7fff), 32767 ) + self.assertEqual( a1.a11_s(-32768), -32768 ) + self.assertEqual( a1.a11_us(0), 0 ) + self.assertEqual( a1.a11_us(0xffff), 65535 ) self.assertEqual( a1.a11_i(-0x80000000), -2147483648 ) self.assertEqual( a1.a11_l(-0x80000000), -2147483648 ) self.assertEqual( a1.a11_ll(-0x80000000), -2147483648 ) @@ -3384,6 +3384,34 @@ def test_94(self): b = None self.assertEqual(r() is None, True) + # range checks + def test_95(self): + + # uses the A single-argument constructor to verify that int ranges + # are tested + a = pya.A.new_a(100) + self.assertEqual(a.a1(), 100) + a = pya.A.new_a(2147483647) + self.assertEqual(a.a1(), 2147483647) + a = pya.A.new_a(-2147483648) + self.assertEqual(a.a1(), -2147483648) + + m = "" + try: + a = pya.A.new_a(2147483648) + self.assertEqual(a.a1(), 2147483648) + except Exception as ex: + m = str(ex) + self.assertEqual(m, "Value out of range: 2147483648, max value is 2147483647 for argument #1 in A.new_a") + + m = "" + try: + a = pya.A.new_a(-2147483649) + self.assertEqual(a.a1(), -2147483649) + except Exception as ex: + m = str(ex) + self.assertEqual(m, "Value out of range: -2147483649, min value is -2147483648 for argument #1 in A.new_a") + # run unit tests if __name__ == '__main__':