瀏覽代碼

Fixing error in json integer limits.
Adding options flags to the parser, so that certain behaviors can be marked as errors.

Samuel Jaffe 8 年之前
父節點
當前提交
7acec3206d

+ 0 - 91
json.xcodeproj/xcuserdata/samjaffe.xcuserdatad/xcschemes/json-test.xcscheme

@@ -1,91 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<Scheme
-   LastUpgradeVersion = "0720"
-   version = "1.3">
-   <BuildAction
-      parallelizeBuildables = "YES"
-      buildImplicitDependencies = "YES">
-      <BuildActionEntries>
-         <BuildActionEntry
-            buildForTesting = "YES"
-            buildForRunning = "YES"
-            buildForProfiling = "YES"
-            buildForArchiving = "YES"
-            buildForAnalyzing = "YES">
-            <BuildableReference
-               BuildableIdentifier = "primary"
-               BlueprintIdentifier = "CDF643311C6E9A8B0016A475"
-               BuildableName = "json-test"
-               BlueprintName = "json-test"
-               ReferencedContainer = "container:json.xcodeproj">
-            </BuildableReference>
-         </BuildActionEntry>
-      </BuildActionEntries>
-   </BuildAction>
-   <TestAction
-      buildConfiguration = "Debug"
-      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
-      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
-      shouldUseLaunchSchemeArgsEnv = "YES">
-      <Testables>
-      </Testables>
-      <MacroExpansion>
-         <BuildableReference
-            BuildableIdentifier = "primary"
-            BlueprintIdentifier = "CDF643311C6E9A8B0016A475"
-            BuildableName = "json-test"
-            BlueprintName = "json-test"
-            ReferencedContainer = "container:json.xcodeproj">
-         </BuildableReference>
-      </MacroExpansion>
-      <AdditionalOptions>
-      </AdditionalOptions>
-   </TestAction>
-   <LaunchAction
-      buildConfiguration = "Debug"
-      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
-      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
-      launchStyle = "0"
-      useCustomWorkingDirectory = "NO"
-      ignoresPersistentStateOnLaunch = "NO"
-      debugDocumentVersioning = "YES"
-      debugServiceExtension = "internal"
-      allowLocationSimulation = "YES">
-      <BuildableProductRunnable
-         runnableDebuggingMode = "0">
-         <BuildableReference
-            BuildableIdentifier = "primary"
-            BlueprintIdentifier = "CDF643311C6E9A8B0016A475"
-            BuildableName = "json-test"
-            BlueprintName = "json-test"
-            ReferencedContainer = "container:json.xcodeproj">
-         </BuildableReference>
-      </BuildableProductRunnable>
-      <AdditionalOptions>
-      </AdditionalOptions>
-   </LaunchAction>
-   <ProfileAction
-      buildConfiguration = "Release"
-      shouldUseLaunchSchemeArgsEnv = "YES"
-      savedToolIdentifier = ""
-      useCustomWorkingDirectory = "NO"
-      debugDocumentVersioning = "YES">
-      <BuildableProductRunnable
-         runnableDebuggingMode = "0">
-         <BuildableReference
-            BuildableIdentifier = "primary"
-            BlueprintIdentifier = "CDF643311C6E9A8B0016A475"
-            BuildableName = "json-test"
-            BlueprintName = "json-test"
-            ReferencedContainer = "container:json.xcodeproj">
-         </BuildableReference>
-      </BuildableProductRunnable>
-   </ProfileAction>
-   <AnalyzeAction
-      buildConfiguration = "Debug">
-   </AnalyzeAction>
-   <ArchiveAction
-      buildConfiguration = "Release"
-      revealArchiveInOrganizer = "YES">
-   </ArchiveAction>
-</Scheme>

+ 5 - 5
json/json_binder_parser.hpp

@@ -19,22 +19,22 @@ namespace json {
   
   namespace parser {
     template <typename T, typename S>
-    void parse(binder::visitor<T, S>&, char const*);
+    void parse(binder::visitor<T, S>&, char const*, options opts = allow_all);
     
     template <typename T, typename S>
-    void parse(binder::visitor<T, S>&& v, std::string const& s) {
-      parse(static_cast<binder::visitor<T, S>&>(v), s.c_str());
+    void parse(binder::visitor<T, S>&& v, std::string const& s, options opts = allow_all) {
+      parse(static_cast<binder::visitor<T, S>&>(v), s.c_str(), opts);
     }
     
     template <typename T, typename S>
-    void parse(binder::visitor<T, S>&& v, std::istream & in) {
+    void parse(binder::visitor<T, S>&& v, std::istream & in, options opts = allow_all) {
       if (!in) return;
       in.seekg(0, std::ios_base::end);
       size_t end = in.tellg();
       std::unique_ptr<char[]> data{new char[end]};
       in.seekg(0);
       in.read(data.get(), end);
-      parse(static_cast<binder::visitor<T, S>&>(v), data.get());
+      parse(static_cast<binder::visitor<T, S>&>(v), data.get(), opts);
     }
   }
 }

+ 2 - 2
json/json_direct_binder.hpp

@@ -19,8 +19,8 @@ namespace json { namespace binder {
       return new direct_binder(*this);
     }
     
-    virtual void parse(T& val, char const*& data) const override {
-      impl.parse(val.*ptr, data);
+    virtual void parse(T& val, char const*& data, parser::options opts) const override {
+      impl.parse(val.*ptr, data, opts);
     }
     
     virtual void write(T const& val, std::ostream & data) const override {

+ 1 - 1
json/json_direct_map_binder.hpp

@@ -34,7 +34,7 @@ namespace json { namespace binder {
         json::helper::advance_to_boundary('}', data);
       }
       if (*data) ++data;
-      else throw json::malformed_json_exception("Reached end of parse string without finding object end");
+      else throw json::unterminated_json_exception("Reached end of parse string without finding object end");
     }
     
     virtual void write(T const& val, std::ostream & data) const override {

+ 4 - 4
json/json_direct_scalar_binder.hpp

@@ -14,7 +14,7 @@ namespace json { namespace binder {
     explicit direct_binder(bool T::*p) : ptr(p) {}
     virtual binder_impl<T>* clone() const override { return new direct_binder(*this); }
     
-    virtual void parse(T& val, char const*& data) const override {
+    virtual void parse(T& val, char const*& data, parser::options) const override {
       if (!strncmp(data, "true", 4)) {
         val.*ptr = true;
       } else if (!strncmp(data, "false", 5)) {
@@ -38,7 +38,7 @@ namespace json { namespace binder {
     explicit direct_binder(int T::*p) : ptr(p) {}
     virtual binder_impl<T>* clone() const override { return new direct_binder(*this); }
     
-    virtual void parse(T& val, char const*& data) const override {
+    virtual void parse(T& val, char const*& data, parser::options) const override {
       json::helper::parse_numeric(val.*ptr, data);
     }
     
@@ -57,7 +57,7 @@ namespace json { namespace binder {
     explicit direct_binder(double T::*p) : ptr(p) {}
     virtual binder_impl<T>* clone() const override { return new direct_binder(*this); }
     
-    virtual void parse(T& val, char const*& data) const override {
+    virtual void parse(T& val, char const*& data, parser::options) const override {
       json::helper::parse_numeric(val.*ptr, data);
     }
     
@@ -76,7 +76,7 @@ namespace json { namespace binder {
     explicit direct_binder(std::string T::*p) : ptr(p) {}
     virtual binder_impl<T>* clone() const override { return new direct_binder(*this); }
     
-    virtual void parse(T& val, char const*& data) const override {
+    virtual void parse(T& val, char const*& data, parser::options) const override {
       json::helper::parse_string(val.*ptr, data);
     }
     

+ 3 - 3
json/json_direct_vector_binder.hpp

@@ -14,7 +14,7 @@ namespace json { namespace binder {
     direct_binder(std::vector<V> T::*p, binder<V> const&i);
     virtual binder_impl<T>* clone() const override { return new direct_binder(*this); }
     
-    virtual void parse(T& val, char const*& data) const override {
+    virtual void parse(T& val, char const*& data, parser::options opts) const override {
       const char ch = json::helper::get_next_element(data);
       if (ch != '[') {
         throw json::malformed_json_exception("Expected an array type");
@@ -24,12 +24,12 @@ namespace json { namespace binder {
       V to_make;
       std::vector<V>& vec = val.*ptr;
       while (*data && *data != ']') {
-        impl.parse(to_make, data);
+        impl.parse(to_make, data, opts);
         vec.emplace_back(to_make);
         json::helper::advance_to_boundary(']', data);
       }
       if (*data) ++data;
-      else throw json::malformed_json_exception("Reached end of parse string without finding array end");
+      else throw json::unterminated_json_exception("Reached end of parse string without finding array end");
     }
     
     virtual void write(T const& val, std::ostream & data) const override {

+ 7 - 6
json/json_object_binder.hpp

@@ -26,10 +26,10 @@ namespace json { namespace binder {
       return *this;
     }
     
-    virtual void parse(T& object, char const*& data) const override {
+    virtual void parse(T& object, char const*& data, parser::options opts) const override {
       const char ch = json::helper::get_next_element(data);
       if (ch == '{') {
-        parse_object(object, ++data);
+        parse_object(object, ++data, opts);
       } else {
         throw json::malformed_json_exception(std::string("Expected an object type for binding to ") + typeid(T).name());
       }
@@ -51,7 +51,7 @@ namespace json { namespace binder {
       data << '}';
     }
     
-    void parse_object(T& object, char const*& data) const {
+    void parse_object(T& object, char const*& data, parser::options opts) const {
       std::string key;
       while (*data && *data != '}') {
         if (json::helper::get_next_element(data) != '"') {
@@ -63,15 +63,16 @@ namespace json { namespace binder {
         }
         auto it = mapping.find(key);
         if (it != mapping.end()) {
-          it->second.parse(object, ++data);
+          it->second.parse(object, ++data, opts);
+        } else if (opts & parser::disable_unknown_keys) {
+          throw json::malformed_json_exception("Unexpected key " + key);
         } else {
           parse_discard_token(++data);
-//          throw json::malformed_json_exception("Unexpected key " + key);
         }
         json::helper::advance_to_boundary('}', data);
       }
       if (*data) ++data;
-      else throw json::malformed_json_exception("Reached end of parse string without finding object end");
+      else throw json::unterminated_json_exception("Reached end of parse string without finding object end");
     }
     
     template <typename E>

+ 5 - 5
json/json_tuple_binder.hpp

@@ -18,10 +18,10 @@ namespace json { namespace binder {
       return *this;
     }
     
-    virtual void parse(T& object, char const*& data) const override {
+    virtual void parse(T& object, char const*& data, parser::options opts) const override {
       const char ch = json::helper::get_next_element(data);
       if (ch == '[') {
-        parse_tuple(object, ++data);
+        parse_tuple(object, ++data, opts);
       } else {
         throw json::malformed_json_exception(std::string("Expected an object type for binding to ") + typeid(T).name());
       }
@@ -41,10 +41,10 @@ namespace json { namespace binder {
       data << ']';
     }
     
-    void parse_tuple(T& object, char const*& data) const {
+    void parse_tuple(T& object, char const*& data, parser::options opts) const {
       auto it = members.begin();
       while (*data && *data != ']' && it != members.end()) {
-        it->parse(object, data);
+        it->parse(object, data, opts);
         json::helper::advance_to_boundary(']', data);
         ++it;
       }
@@ -52,7 +52,7 @@ namespace json { namespace binder {
         throw json::malformed_json_exception("Failed to parse every member of tuple");
       }
       if (*data) ++data;
-      else throw json::malformed_json_exception("Reached end of parse string without finding array end");
+      else throw json::unterminated_json_exception("Reached end of parse string without finding array end");
     }
     
     template <typename E>

+ 18 - 31
json_binder.hpp

@@ -24,43 +24,27 @@ namespace json {
     public:
       virtual binder_impl<T>* clone() const = 0;
       virtual ~binder_impl() {}
-      virtual void parse(T&, char const*&) const = 0;
+      virtual void parse(T&, char const*&, parser::options) const = 0;
       virtual void write(T const&, std::ostream &) const = 0;
     };
     
     template <typename T>
     class binder {
     public:
-      binder() :
-      impl(nullptr) {
-      }
-      
-      binder(binder const& other) :
-      impl(other.impl->clone()) {
-      }
-      
-      explicit binder(binder_impl<T> const* p) :
-      impl(p) {
-      }
+      binder() : impl(nullptr) {}
+      binder(binder const& other) : impl(other.impl->clone()) {}
+      explicit binder(binder_impl<T> const* p) : impl(p) {}
+      explicit binder(binder_impl<T> const& r) : impl(r.clone()) {}
       
-      explicit binder(binder_impl<T> const& r) :
-      impl(r.clone()) {
-      }
+      ~binder() { delete impl; }
       
-      ~binder() {
-        if (impl) {
-          delete impl;
-          impl = nullptr;
-        }
-      }
-      
-      void parse(T& object, char const*& data) const {
-        if (!impl) return;
-        impl->parse(object, data);
+      void parse(T& object, char const*& data, parser::options opts) const {
+        if (!impl) return; // error?
+        impl->parse(object, data, opts);
       }
       
       void write(T const& object, std::ostream & data) const {
-        if (!impl) return;
+        if (!impl) return; // error?
         impl->write(object, data);
       }
     private:
@@ -72,8 +56,11 @@ namespace json {
     public:
       visitor(S& o, binder_impl<T>& b) : obj(o), b(b) {}
       
-      void parse(char const* data) {
-        b.parse(obj, data);
+      void parse(char const* data, parser::options opts) {
+        b.parse(obj, data, opts);
+        if ( json::helper::get_next_element(data) && opts & parser::disable_concatenated_json_bodies ) {
+          throw malformed_json_exception{"Config set to require json input be terminated"};
+        }
       }
       
       void write(std::ostream & data) const {
@@ -91,10 +78,10 @@ namespace json {
     }
   }
   
-  namespace parser {
+  namespace parser {    
     template <typename T>
-    void parse(binder::visitor<T>& visitor, char const* data) {
-      visitor.parse(data);
+    void parse(binder::visitor<T>& visitor, char const* data, options opts = allow_all) {
+      visitor.parse(data, opts);
     }
         
     template <typename T, typename S>

+ 48 - 35
json_common.hpp

@@ -26,51 +26,64 @@ namespace json {
     using std::domain_error::domain_error;
   };
   
+  class unterminated_json_exception :
+  public malformed_json_exception {
+    using malformed_json_exception::malformed_json_exception;
+  };
+  
   namespace {
-    const constexpr json::int_jt INT_JT_MAX_LAST_DIGIT = (0x7FFFFFFF % 10);
-    const constexpr json::int_jt INT_JT_MAX = json::uint_jt(-1) - 1;
-    const constexpr json::int_jt INT_JT_MIN = json::int_jt(~(json::uint_jt(-1) / 2));
+    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);
+    const constexpr json::int_jt INT_JT_MIN = std::numeric_limits<json::int_jt>::min();
     const constexpr json::uint_jt INT_JT_OVER = json::uint_jt(INT_JT_MAX) + 1;
     const constexpr json::uint_jt UINT_JT_MAX = json::uint_jt(0) - 1;
     //  const constexpr json::uint_jt UINT_JT_MIN = 0;
   }
 }
 
-namespace json {
-  namespace helper {
-    const char get_next_element(char const*& data);
-    
-    enum numeric_state {
-      DOUBLE, INTEGER
-    };
-
-    struct numeric_token_info {
-      numeric_token_info(char const * start);
-      numeric_state parse_numeric();
-      
-      uint_jt val;
+namespace json { namespace parser {
+  enum options {
+    allow_all                        = 0x00,
+    disable_unknown_keys             = 0x01,
+    disable_concatenated_json_bodies = 0x02,
+    disable_all                      = 0xFF,
+  };
+} }
 
-      char const * it;
-      char const * end;
-      bool is_double;
-      bool is_negative;
-    };
-    
-    numeric_token_info get_numeric_token_info(char const * it);
+namespace json { namespace helper {
+  const char get_next_element(char const*& data);
+  
+  enum numeric_state {
+    DOUBLE, INTEGER
+  };
+  
+  struct numeric_token_info {
+    numeric_token_info(char const * start);
+    numeric_state parse_numeric();
     
-    /**
-     * @throws json::malformed_json_exception
-     */
-    void advance_to_boundary(char const endtok, char const *& data);
+    uint_jt val;
     
-    /**
-     * @throws json::malformed_json_exception
-     */
-    std::string parse_string(char const * & data);
-        
-    template <typename T>
-    void parse_string(T& json, char const * & data) {
-      json = parse_string(data);
+    char const * it;
+    char const * end;
+    bool is_double;
+    bool is_negative;
+  };
+  
+  numeric_token_info get_numeric_token_info(char const * it);
+  
+  /**
+   * @throws json::malformed_json_exception
+   */
+  void advance_to_boundary(char const endtok, char const *& data);
+  
+  /**
+   * @throws json::malformed_json_exception
+   */
+  std::string parse_string(char const * & data);
+  
+  template <typename T>
+  void parse_string(T& json, char const * & data) {
+    json = parse_string(data);
     }
     
     template <typename T>