Explorar el Código

refactor: make type enums less clever

Sam Jaffe hace 1 año
padre
commit
1245814edc

+ 6 - 4
include/jvalidate/adapter.h

@@ -8,6 +8,7 @@
 #include <string_view>
 
 #include <jvalidate/detail/array_iterator.h>
+#include <jvalidate/detail/number.h>
 #include <jvalidate/detail/object_iterator.h>
 #include <jvalidate/enum.h>
 #include <jvalidate/forward.h>
@@ -34,10 +35,6 @@ public:
 
   virtual bool equals(Adapter const & lhs, bool strict) const = 0;
 
-  static bool is_integer(double value) {
-    return std::floor(value) == value && std::abs(value) <= std::numeric_limits<int64_t>::max();
-  }
-
   bool maybe_null(bool strict) const {
     switch (type()) {
     case Type::Null:
@@ -71,6 +68,11 @@ public:
 
   std::optional<int64_t> maybe_integer(bool strict) const {
     switch (type()) {
+    case Type::Number:
+      if (double value = as_number(); jvalidate::detail::fits_in_integer(value)) {
+        return static_cast<int64_t>(value);
+      }
+      return std::nullopt;
     case Type::Integer:
       return as_integer();
     case Type::Boolean:

+ 6 - 5
include/jvalidate/adapters/jsoncpp.h

@@ -6,6 +6,7 @@
 #include <json/value.h>
 
 #include <jvalidate/adapter.h>
+#include <jvalidate/detail/number.h>
 #include <jvalidate/detail/simple_adapter.h>
 #include <jvalidate/enum.h>
 
@@ -43,16 +44,15 @@ public:
   using JsonCppAdapter::SimpleAdapter::SimpleAdapter;
 
   Type type() const {
+    using jvalidate::detail::fits_in_integer;
     switch (const_value().type()) {
     case Json::nullValue:
       return Type::Null;
     case Json::booleanValue:
       return Type::Boolean;
     case Json::realValue:
-      if (Adapter::is_integer(as_number())) {
-        return Type::Integer;
-      }
-      return Type::Number;
+      // Not strictly necessary - Validation code will safely handle this too
+      return fits_in_integer(const_value().asDouble()) ? Type::Integer : Type::Number;
     case Json::stringValue:
       return Type::String;
     case Json::arrayValue:
@@ -60,8 +60,9 @@ public:
     case Json::objectValue:
       return Type::Object;
     case Json::intValue:
-    case Json::uintValue:
       return Type::Integer;
+    case Json::uintValue:
+      return fits_in_integer(const_value().asUInt64()) ? Type::Integer : Type::Number;
     }
   }
 

+ 16 - 0
include/jvalidate/detail/number.h

@@ -0,0 +1,16 @@
+#pragma once
+
+#include <cmath>
+#include <limits>
+
+namespace jvalidate::detail {
+inline bool is_json_integer(double number) { return std::floor(number) == number; }
+
+inline bool fits_in_integer(double number) {
+  static constexpr double g_int_max = std::numeric_limits<int64_t>::max();
+  static constexpr double g_int_min = std::numeric_limits<int64_t>::min();
+  return is_json_integer(number) && number <= g_int_max && number >= g_int_min;
+}
+
+inline bool fits_in_integer(uint64_t number) { return (number & 0x8000'0000'0000'0000) == 0; }
+}

+ 7 - 12
include/jvalidate/enum.h

@@ -4,19 +4,14 @@
 
 namespace jvalidate::adapter {
 enum class Type : int8_t {
-  Null = 0b0000001,
-  Boolean = 0b0000010,
-  Integer = 0b0000100,
-  Number = 0b0001100,
-  String = 0b0010000,
-  Array = 0b0100000,
-  Object = 0b1000000,
+  Null,
+  Boolean,
+  Integer,
+  Number,
+  String,
+  Array,
+  Object,
 };
-
-inline bool operator&(Type lhs, Type rhs) {
-  return static_cast<bool>((static_cast<int>(lhs) & static_cast<int>(rhs)) ==
-                           static_cast<int>(rhs));
-}
 }
 
 namespace jvalidate::schema {

+ 14 - 3
include/jvalidate/validation_visitor.h

@@ -11,6 +11,7 @@
 #include <jvalidate/constraint/visitor.h>
 #include <jvalidate/detail/expect.h>
 #include <jvalidate/detail/iostream.h>
+#include <jvalidate/detail/number.h>
 #include <jvalidate/detail/pointer.h>
 #include <jvalidate/forward.h>
 #include <jvalidate/schema.h>
@@ -20,7 +21,8 @@
 
 #define VISITED(type) std::get<std::unordered_set<type>>(*visited_)
 
-#define NOOP_UNLESS_TYPE(etype) RETURN_UNLESS(adapter::Type::etype & document_.type(), Status::Noop)
+#define NOOP_UNLESS_TYPE(etype)                                                                    \
+  RETURN_UNLESS(adapter::Type::etype == document_.type(), Status::Noop)
 
 #define BREAK_EARLY_IF_NO_RESULT_TREE()                                                            \
   do {                                                                                             \
@@ -57,7 +59,14 @@ public:
   Status visit(constraint::TypeConstraint const & cons) const {
     adapter::Type const type = document_.type();
     for (adapter::Type const accept : cons.types) {
-      if (accept & type) {
+      if (type == accept) {
+        return Status::Accept;
+      }
+      if (accept == adapter::Type::Number && type == adapter::Type::Integer) {
+        return Status::Accept;
+      }
+      if (accept == adapter::Type::Integer && type == adapter::Type::Number &&
+          detail::is_json_integer(document_.as_number())) {
         return Status::Accept;
       }
     }
@@ -181,7 +190,9 @@ public:
   }
 
   Status visit(constraint::MultipleOfConstraint const & cons) const {
-    NOOP_UNLESS_TYPE(Number);
+    adapter::Type const type = document_.type();
+    RETURN_UNLESS(type == adapter::Type::Number || type == adapter::Type::Integer, Status::Noop);
+
     if (double value = document_.as_number(); not cons(value)) {
       add_error("number ", value, " is not a multiple of ", cons.value);
       return false;