Browse Source

rename ParseError to JSONError
check for overflow when parsing numbers


git-svn-id: svn://bind10.isc.org/svn/bind10/branches/trac172@2114 e5f2f494-b856-4b98-b285-d166d9295462

Jelte Jansen 15 years ago
parent
commit
c5dd3881c2
3 changed files with 59 additions and 47 deletions
  1. 41 37
      src/lib/cc/data.cc
  2. 6 6
      src/lib/cc/data.h
  3. 12 4
      src/lib/cc/data_unittests.cc

+ 41 - 37
src/lib/cc/data.cc

@@ -26,6 +26,8 @@
 
 #include <boost/algorithm/string.hpp> // for iequals
 
+#include <cmath>
+
 using namespace std;
 
 namespace {
@@ -201,14 +203,14 @@ Element::find(const std::string& identifier UNUSED_PARAM,
 
 namespace {
 inline void
-throwParseError(const std::string& error, const std::string& file, int line = 0, int pos = 0)
+throwJSONError(const std::string& error, const std::string& file, int line = 0, int pos = 0)
 {
     if (line != 0 || pos != 0) {
         std::stringstream ss;
         ss << error << " in " + file + ":" << line << ":" << pos;
-        throw ParseError(ss.str());
+        throw JSONError(ss.str());
     } else {
-        throw ParseError(error);
+        throw JSONError(error);
     }
 }
 }
@@ -321,17 +323,17 @@ skip_to(std::istream &in, const std::string& file, int& line,
             --pos;
             return;
         } else {
-            throwParseError(std::string("'") + c + "' read, one of \"" + chars + "\" expected", file, line, pos);
+            throwJSONError(std::string("'") + c + "' read, one of \"" + chars + "\" expected", file, line, pos);
         }
     }
-    throwParseError(std::string("EOF read, one of \"") + chars + "\" expected", file, line, pos);
+    throwJSONError(std::string("EOF read, one of \"") + chars + "\" expected", file, line, pos);
 }
 
 // TODO: Should we check for all other official escapes here (and
 // error on the rest)?
 std::string
 str_from_stringstream(std::istream &in, const std::string& file, const int line,
-                      int& pos) throw (ParseError)
+                      int& pos) throw (JSONError)
 {
     char c = 0;
     std::stringstream ss;
@@ -341,7 +343,7 @@ str_from_stringstream(std::istream &in, const std::string& file, const int line,
         c = in.get();
         ++pos;
     } else {
-        throwParseError("String expected", file, line, pos);
+        throwJSONError("String expected", file, line, pos);
     }
     while (c != EOF && c != '"') {
         ss << c;
@@ -375,7 +377,9 @@ count_chars_i(int i) {
     return result;
 }
 
-// TODO: range checks
+// Should we change from IntElement and DoubleElement to NumberElement
+// that can also hold an e value? (and have specific getters if the
+// value is larger than an int can handle)
 ElementPtr
 from_stringstream_number(std::istream &in, int &pos) {
     int i, d_i;
@@ -384,43 +388,43 @@ from_stringstream_number(std::istream &in, int &pos) {
 
     in >> i;
     pos += count_chars_i(i);
+    if (in.fail()) {
+        throw JSONError("Bad integer or overflow");
+    }
     if (in.peek() == '.') {
         is_double = true;
         in.get();
         pos++;
         in >> d_i;
+        if (in.fail()) {
+            throw JSONError("Bad real or overflow");
+        }
         d = i + (double)d_i / 10;
         pos += count_chars_i(d_i);
     }
     if (in.peek() == 'e' || in.peek() == 'E') {
         int e;
+        double p;
         in.get();
         pos++;
         in >> e;
+        if (in.fail()) {
+            throw JSONError("Bad exponent or overflow");
+        }
         pos += count_chars_i(e);
-        if (e == 0) {
-            d = 1;
-            i = 1;
-        } else if (e < 0) {
-            if (!is_double) {
-                is_double = true;
-                d = i;
-            }
-            while (e < 0) {
-                d = d / 10;
-                e++;
-            }
+        p = pow(10, e);
+        if (p == HUGE_VAL) {
+            throw JSONError("Bad exponent or overflow");
+        }
+        if (is_double) {
+            d = d * p;
         } else {
-            if (is_double) {
-                while (e > 0) {
-                    d = d * 10;
-                    e--;
-                }
+            if (p > 1.0) {
+                i = i * p;
             } else {
-                while (e > 0) {
-                    i = i * 10;
-                    e--;
-                }
+                // negative exponent, so type becomes a double
+                is_double = true;
+                d = i * p;
             }
         }
     }
@@ -442,7 +446,7 @@ from_stringstream_bool(std::istream &in, const std::string& file,
     } else if (boost::iequals(word, "False")) {
         return Element::create(false);
     } else {
-        throwParseError(std::string("Bad boolean value: ") + word, file, line, pos);
+        throwJSONError(std::string("Bad boolean value: ") + word, file, line, pos);
         // above is a throw shortcur, return empty is never reached
         return ElementPtr();
     }
@@ -456,7 +460,7 @@ from_stringstream_null(std::istream &in, const std::string& file,
     if (boost::iequals(word, "null")) {
         return Element::create();
     } else {
-        throwParseError(std::string("Bad null value: ") + word, file, line, pos);
+        throwJSONError(std::string("Bad null value: ") + word, file, line, pos);
         return ElementPtr();
     }
 }
@@ -503,7 +507,7 @@ from_stringstream_map(std::istream &in, const std::string& file, int& line,
             if (key.length() > 255) {
                 // Map tag has one-byte length field in wire format, so the
                 // length cannot exceed 255.
-                throwParseError("Map tag is too long", file, line, pos);
+                throwJSONError("Map tag is too long", file, line, pos);
             }
 
             skip_to(in, file, line, pos, ":", " \t\n");
@@ -524,20 +528,20 @@ from_stringstream_map(std::istream &in, const std::string& file, int& line,
 }
 
 ElementPtr
-Element::fromJSON(std::istream& in) throw(ParseError) {
+Element::fromJSON(std::istream& in) throw(JSONError) {
     int line = 1, pos = 1;
     return fromJSON(in, "<istream>", line, pos);
 }
 
 ElementPtr
-Element::fromJSON(std::istream& in, const std::string& file_name) throw(ParseError)
+Element::fromJSON(std::istream& in, const std::string& file_name) throw(JSONError)
 {
     int line = 1, pos = 1;
     return fromJSON(in, file_name, line, pos);
 }
 
 ElementPtr
-Element::fromJSON(std::istream &in, const std::string& file, int& line, int& pos) throw(ParseError)
+Element::fromJSON(std::istream &in, const std::string& file, int& line, int& pos) throw(JSONError)
 {
     char c = 0;
     ElementPtr element;
@@ -590,14 +594,14 @@ Element::fromJSON(std::istream &in, const std::string& file, int& line, int& pos
             case EOF:
                 break;
             default:
-                throwParseError(std::string("error: unexpected character ") + c, file, line, pos);
+                throwJSONError(std::string("error: unexpected character ") + c, file, line, pos);
                 break;
         }
     }
     if (el_read) {
         return element;
     } else {
-        throw ParseError("nothing read");
+        throw JSONError("nothing read");
     }
 }
 

+ 6 - 6
src/lib/cc/data.h

@@ -48,9 +48,9 @@ public:
 // i'd like to use Exception here but we need one that is derived from
 // runtime_error (as this one is directly based on external data, and
 // i want to add some values to any static data string that is provided)
-class ParseError : public std::runtime_error {
+class JSONError : public std::runtime_error {
 public:
-    ParseError(const std::string &err) : std::runtime_error(err) {};
+    JSONError(const std::string &err) : std::runtime_error(err) {};
 };
 
 ///
@@ -298,7 +298,7 @@ public:
 
     /// \brief These functions will parse the given string (JSON)
     /// representation  of a compound element. If there is a parse
-    /// error, an exception of the type isc::data::ParseError is thrown.
+    /// error, an exception of the type isc::data::JSONError is thrown.
 
     //@{
     /// Creates an Element from the given JSON string
@@ -313,8 +313,8 @@ public:
     /// \param in The string to parse the element from
     /// \return An ElementPtr that contains the element(s) specified
     /// in the given input stream.
-    static ElementPtr fromJSON(std::istream& in) throw(ParseError);
-    static ElementPtr fromJSON(std::istream& in, const std::string& file_name) throw(ParseError);
+    static ElementPtr fromJSON(std::istream& in) throw(JSONError);
+    static ElementPtr fromJSON(std::istream& in, const std::string& file_name) throw(JSONError);
 
     /// Creates an Element from the given input stream, where we keep
     /// track of the location in the stream for error reporting.
@@ -327,7 +327,7 @@ public:
     /// \return An ElementPtr that contains the element(s) specified
     /// in the given input stream.
     // make this one private?
-    static ElementPtr fromJSON(std::istream& in, const std::string& file, int& line, int &pos) throw(ParseError);
+    static ElementPtr fromJSON(std::istream& in, const std::string& file, int& line, int &pos) throw(JSONError);
     //@}
 
     /// \name Wire format factory functions

+ 12 - 4
src/lib/cc/data_unittests.cc

@@ -44,9 +44,10 @@ TEST(Element, type) {
     EXPECT_EQ(list_el.getType(), Element::list);
     MapElement map_el = MapElement();
     EXPECT_EQ(map_el.getType(), Element::map);
+
 }
 
-TEST(Element, from_and_to_str) {
+TEST(Element, from_and_to_json) {
     // this test checks whether the str() method returns the same
     // string that was used for creation
     ElementPtr el;
@@ -73,7 +74,7 @@ TEST(Element, from_and_to_str) {
     // some parse errors
     try {
         Element::fromJSON("{1}");
-    } catch (isc::data::ParseError pe) {
+    } catch (isc::data::JSONError pe) {
         std::string s = std::string(pe.what());
         EXPECT_EQ(s, "String expected in <string>:1:3");
     }
@@ -89,7 +90,7 @@ TEST(Element, from_and_to_str) {
     sv.push_back("");
     BOOST_FOREACH(std::string s, sv) {
         
-        EXPECT_THROW(el = Element::fromJSON(s), isc::data::ParseError);
+        EXPECT_THROW(el = Element::fromJSON(s), isc::data::JSONError);
     }
 
     // some json specific format tests, here the str() output is
@@ -102,6 +103,13 @@ TEST(Element, from_and_to_str) {
     EXPECT_EQ("100", Element::fromJSON("1.0e2")->str());
     EXPECT_EQ("0.01", Element::fromJSON("1.0e-2")->str());
     EXPECT_EQ("0.012", Element::fromJSON("1.2e-2")->str());
+
+    EXPECT_THROW(Element::fromJSON("12345678901234567890")->str(), JSONError);
+    EXPECT_THROW(Element::fromJSON("1.12345678901234567890")->str(), JSONError);
+    EXPECT_THROW(Element::fromJSON("1.1e12345678901234567890")->str(), JSONError);
+    EXPECT_THROW(Element::fromJSON("1e12345678901234567890")->str(), JSONError);
+    EXPECT_THROW(Element::fromJSON("1e50000")->str(), JSONError);
+
 }
 
 TEST(Element, create_and_value_throws) {
@@ -231,7 +239,7 @@ TEST(Element, MapElement) {
     long_maptag.push_back('f');
     EXPECT_THROW(Element::fromJSON("{ \"" + long_maptag +
                                            "\": \"bar\"}"),
-                 ParseError);
+                 JSONError);
 
     EXPECT_THROW(el->set(long_maptag, Element::create("bar")), TypeError);