Преглед на файлове

Adding support for integral types shorter than int32_t, and floating point types other than double.
TODO: support longer integers

Samuel Jaffe преди 8 години
родител
ревизия
227e4ac9c6
променени са 5 файла, в които са добавени 122 реда и са изтрити 48 реда
  1. 22 0
      json.cpp
  2. 15 1
      json/json_binder_discard.cpp
  3. 1 1
      json/json_direct_binder.hpp
  4. 8 12
      json/json_direct_scalar_binder.hpp
  5. 76 34
      json_common.hpp

+ 22 - 0
json.cpp

@@ -10,6 +10,28 @@
 
 const json::value json::value::null_value{};
 
+template <>
+void json::helper::parse_numeric(json::value & json, char const * & data) {
+  numeric_token_info info = data;
+  if ( info.is_double || info.parse_numeric() == DOUBLE ) {
+    helper::parse_double(json, data);
+  } else {
+    uint_jt const val = info.val;
+    if (info.is_negative) {
+      if (val == INT_JT_OVER) {
+        json = INT_JT_MIN;
+      } else {
+        json = -int_jt(val);
+      }
+    } else if (val <= uint_jt(INT_JT_MAX)) {
+      json = int_jt(val);
+    } else {
+      json = val;
+    }
+    data = info.it;
+  }
+}
+
 json::value& json::value::operator[](const size_t idx) {
   if (!is_array()) {
     data.set<array_jt>();

+ 15 - 1
json/json_binder_discard.cpp

@@ -14,7 +14,11 @@ namespace json { namespace {
     template <typename T> discard_t & operator= (T const &) { return *this; }
     template <typename T> discard_t & operator[](T const &) { return *this; }
   };
-  
+} }
+template <>
+void json::helper::parse_numeric<json::discard_t>(json::discard_t &, char const * & data);
+
+namespace json { namespace {
   void parse_object(discard_t& json, char const*& data);
   void parse_array(discard_t& json, char const*& data);
   void parse_one_token(discard_t& json, char const*& data);
@@ -62,6 +66,16 @@ namespace json { namespace {
 } }
 
 namespace json {
+  template <>
+  void helper::parse_numeric<discard_t>(discard_t & d, char const * & data) {
+    numeric_token_info info = data;
+    if ( info.is_double || info.parse_numeric() == DOUBLE ) {
+      helper::parse_double(d, data);
+    } else {
+      data = info.it;
+    }
+  }
+  
   void parse_discard_token( char const * & data ) {
     json::discard_t tmp;
     parse_one_token( tmp, data );

+ 1 - 1
json/json_direct_binder.hpp

@@ -8,7 +8,7 @@
 #pragma once
 
 namespace json { namespace binder {
-  template <typename T, typename E>
+  template <typename T, typename E, typename = void>
   class direct_binder : public binder_impl<T> {
   public:
     direct_binder(E T::*p, binder<E> const& i) :

+ 8 - 12
json/json_direct_scalar_binder.hpp

@@ -32,10 +32,10 @@ namespace json { namespace binder {
     bool T::*ptr;
   };
   
-  template <typename T>
-  class direct_binder<T, int> : public binder_impl<T> {
+  template <typename T, typename N>
+  class direct_binder<T, N, typename std::enable_if<std::is_integral<N>::value>::type> : public binder_impl<T> {
   public:
-    explicit direct_binder(int T::*p) : ptr(p) {}
+    explicit direct_binder(N T::*p) : ptr(p) {}
     virtual binder_impl<T>* clone() const override { return new direct_binder(*this); }
     
     virtual void parse(T& val, char const*& data, parser::options) const override {
@@ -43,18 +43,16 @@ namespace json { namespace binder {
     }
     
     virtual void write(T const& val, std::ostream & data) const override {
-//      char buffer[16] = { '\0' };
-//      snprintf(buffer, sizeof(buffer), "%d", val.*ptr);
       data << val.*ptr;
     }
   private:
-    int T::*ptr;
+    N T::*ptr;
   };
   
-  template <typename T>
-  class direct_binder<T, double> : public binder_impl<T> {
+  template <typename T, typename N>
+  class direct_binder<T, N, typename std::enable_if<std::is_floating_point<N>::value>::type> : public binder_impl<T> {
   public:
-    explicit direct_binder(double T::*p) : ptr(p) {}
+    explicit direct_binder(N T::*p) : ptr(p) {}
     virtual binder_impl<T>* clone() const override { return new direct_binder(*this); }
     
     virtual void parse(T& val, char const*& data, parser::options) const override {
@@ -62,12 +60,10 @@ namespace json { namespace binder {
     }
     
     virtual void write(T const& val, std::ostream & data) const override {
-//      char buffer[32] = { '\0' };
-//      snprintf(buffer, sizeof(buffer), "%lf", );
       data << val.*ptr;
     }
   private:
-    double T::*ptr;
+    N T::*ptr;
   };
   
   template <typename T>

+ 76 - 34
json_common.hpp

@@ -31,6 +31,16 @@ namespace json {
     using malformed_json_exception::malformed_json_exception;
   };
   
+  class json_numeric_exception :
+  public std::domain_error {
+    using std::domain_error::domain_error;
+  };
+
+  class json_numeric_width_exception :
+  public json_numeric_exception {
+    using json_numeric_exception::json_numeric_exception;
+  };
+
   namespace {
     const constexpr json::int_jt INT_JT_MAX = std::numeric_limits<json::int_jt>::max();
     const constexpr json::int_jt INT_JT_MAX_LAST_DIGIT = (INT_JT_MAX % 10);
@@ -84,45 +94,77 @@ namespace json { namespace helper {
   template <typename T>
   void parse_string(T& json, char const * & data) {
     json = parse_string(data);
+  }
+  
+  template <typename T>
+  void parse_double_impl(T & json, char const * begin, char const * end) {
+    json = std::stod(std::string(begin, end));
+  }
+  template <>
+  inline void parse_double_impl<float>(float & json, char const * begin, char const * end) {
+    json = std::stof(std::string(begin, end));
+  }
+  template <>
+  inline void parse_double_impl<long double>(long double & json, char const * begin, char const * end) {
+    json = std::stold(std::string(begin, end));
+  }
+  
+  template <typename T>
+  void parse_double(T& json, char const * & data) {
+    char const * begin = data;
+    if (*data == '-') { ++data; }
+    while (isdigit(*data) || *data == '-') { ++data; }
+    if (*data == '.') { ++data; }
+    else if (begin != data && (*data == 'e' || *data == 'E')) {
+      ++data;
+      if (*data == '-') { ++data; }
     }
+    while (isdigit(*data)) { ++data; }
+    if ( begin == data ) {
+      throw json::json_numeric_exception{"Expected numeric data"};
+    }
+    try {
+      parse_double_impl(json, begin, data);
+    } catch (std::domain_error const &) {
+      throw json::json_numeric_exception{"Expected a numeric type"};
+    } catch (std::out_of_range const &) {
+      throw json::json_numeric_width_exception{"Number is out-of-range for floating-point type"};
+    }
+  }
+  
+  template <typename J>
+  void parse_numeric(J & json, char const * & data) {
+    numeric_token_info info = data;
     
-    template <typename T>
-    void parse_double(T& json, char const * & data) {
-      json = atof(data);
-      while (isdigit(*data)) { ++data; }
-      if (*data == '.' || *data == 'e') { ++data; }
-      while (isdigit(*data)) { ++data; }
-   }
-    
-//    template <typename T>
-//    void parse_integer(T& json, char const * & data) {
-//      json = atoi(data);
-//      while (isdigit(*data)) { ++data; }
-//    }
-    
-    template <typename J>
-    void parse_numeric(J & json, char const * & data) {
-      numeric_token_info info = data;
+    if ( info.is_double || info.parse_numeric() == DOUBLE ) {
+      if (std::is_integral<J>::value) {
+        throw json::json_numeric_exception{"Expected integer, got double"};
+      }
+      helper::parse_double(json, data);
+    } else {
+      uint_jt const val = info.val;
       
-      if ( info.is_double ) {
-        helper::parse_double(json, data);
-      } else if ( info.parse_numeric() == DOUBLE ) {
-        helper::parse_double(json, data);
-      } else {
-        uint_jt const val = info.val;
-        if (info.is_negative) {
-          if (val == INT_JT_OVER) {
-            json = INT_JT_MIN;
-          } else {
-            json = -int_jt(val);
-          }
-        } else if (val <= uint_jt(INT_JT_MAX)) {
-          json = int_jt(val);
+      if (fls(val) > std::numeric_limits<J>::digits + info.is_negative) {
+        throw json::json_numeric_width_exception{"Integer width too small for parsed value"};
+      }
+
+      if (info.is_negative) {
+        if (!std::is_signed<J>::value) {
+          throw json::json_numeric_exception{"Expected signed integer"};
+        }
+        if (val == INT_JT_OVER) {
+          json = (J) INT_JT_MIN; // Cast suppresses error message, narrowing check above makes safe
         } else {
-          json = val;
+          json = -int_jt(val);
         }
-        data = info.it;
+      } else if (val <= uint_jt(INT_JT_MAX)) {
+        json = int_jt(val);
+      } else if (std::is_signed<J>::value) {
+        throw json::json_numeric_exception{"Expected unsigned integer"};
+      } else {
+        json = val;
       }
+      data = info.it;
     }
   }
-}
+} }