فهرست منبع

refactor: remove weird caching thing for dynamic refs

Sam Jaffe 1 سال پیش
والد
کامیت
e3a2b709d7
2فایلهای تغییر یافته به همراه50 افزوده شده و 68 حذف شده
  1. 20 7
      include/jvalidate/reference_handler.h
  2. 30 61
      include/jvalidate/schema.h

+ 20 - 7
include/jvalidate/reference_handler.h

@@ -24,7 +24,6 @@ private:
   detail::ReferenceCache references_;
 
   std::multimap<URI, detail::Anchor> dynamic_anchors_;
-  std::map<detail::Anchor, detail::Reference> active_dynamic_anchors_;
 
 public:
   ReferenceManager(A const & root, schema::Version version, Keywords const & keywords)
@@ -32,6 +31,15 @@ public:
     prime_impl(root, {}, version, keywords);
   }
 
+  bool has_dynamic_anchor(detail::RootReference const & root) const {
+    for (auto [it, end] = dynamic_anchors_.equal_range(root.uri()); it != end; ++it) {
+      if (it->second == root.anchor()) {
+        return true;
+      }
+    }
+    return false;
+  }
+
   std::optional<A> root(detail::RootReference const & root) const {
     if (auto it = roots_.find(root); it != roots_.end()) {
       return it->second;
@@ -47,7 +55,6 @@ public:
                                ref.pointer());
     }
 
-    // TODO(samjaffe): Clean this clause up
     URI uri = [this, &ref, &parent]() {
       if (ref.uri().empty() && parent.uri().empty()) {
         return references_.actual_parent_uri(parent);
@@ -141,17 +148,23 @@ private:
       where = references_.emplace(where, root);
     }
 
-    std::string const dyn_anchor =
-        version > schema::Version::Draft2019_09 ? "$dynamicAnchor" : "$recursiveAnchor";
+    if (version == schema::Version::Draft2019_09 && schema.contains("$recursiveAnchor") &&
+        schema["$recursiveAnchor"].as_boolean()) {
+      detail::Anchor anchor;
+      root = detail::RootReference(root.uri(), anchor);
+
+      roots_.emplace(root, json);
+      where = references_.emplace(where, root);
+      dynamic_anchors_.emplace(root.uri(), anchor);
+    }
 
-    if (schema.contains(dyn_anchor) && version == schema::Version::Draft2019_09) {
-      detail::Anchor anchor(schema[dyn_anchor].as_string());
+    if (schema.contains("$dynamicAnchor") && version > schema::Version::Draft2019_09) {
+      detail::Anchor anchor(schema["$dynamicAnchor"].as_string());
       root = detail::RootReference(root.uri(), anchor);
 
       roots_.emplace(root, json);
       where = references_.emplace(where, root);
       dynamic_anchors_.emplace(root.uri(), anchor);
-      active_dynamic_anchors_.emplace(anchor, root);
     }
   }
 };

+ 30 - 61
include/jvalidate/schema.h

@@ -24,7 +24,6 @@ private:
   std::string description_;
   std::unique_ptr<adapter::Const const> default_{nullptr};
 
-  detail::Reference uri_;
   std::optional<std::string> rejects_all_;
   std::optional<schema::Node const *> reference_{};
   std::unordered_map<std::string, std::unique_ptr<constraint::Constraint>> constraints_{};
@@ -109,26 +108,11 @@ private:
   friend class schema::Node;
   template <Adapter A> friend class detail::ParserContext;
 
-  struct DynamicRef {
-    template <typename F>
-    DynamicRef(detail::Reference const & where, F const & reconstruct)
-        : where(where), reconstruct(reconstruct) {}
-
-    detail::Reference where;
-    std::function<schema::Node const *()> reconstruct;
-  };
-
 private:
   schema::Node accept_;
   schema::Node reject_{"always false"};
 
-  // A map of anchors to DynamicRef info - note that DynamicRef.reconstruct is
-  // an unsafe object, because it holds an object which may hold references to
-  // temporary objects.
-  // Nothing should be added to this object except through calling
-  // {@see Node::resolve_anchor}, which returns a scope(exit) construct that
-  // cleans up the element.
-  std::map<detail::Anchor, DynamicRef> dynamic_anchors_;
+  std::map<detail::Anchor, detail::Reference> dynamic_anchors_;
 
   // An owning cache of all created schemas. Avoids storing duplicates such as
   // the "always-true" schema, "always-false" schema, and schemas whose only
@@ -231,19 +215,6 @@ public:
       : Schema(adapter::AdapterFor<JSON const>(json), std::forward<Args>(args)...) {}
 
 private:
-  template <Adapter A>
-  void dynamic_anchor(detail::Anchor const & anchor, detail::ParserContext<A> const & context) {
-    dynamic_anchors_.try_emplace(anchor, context.where,
-                                 [this, context]() { return fetch_schema(context); });
-  }
-
-  void remove_dynamic_anchor(detail::Anchor const & anchor, detail::Reference const & where) {
-    if (auto it = dynamic_anchors_.find(anchor);
-        it != dynamic_anchors_.end() && it->second.where == where) {
-      dynamic_anchors_.erase(it);
-    }
-  }
-
   schema::Node const * alias(detail::Reference const & where, schema::Node const * schema) {
     alias_cache_.emplace(where, schema);
     return schema;
@@ -278,7 +249,7 @@ private:
 
     context.ref.prime(*schema, ref.uri(), context.version,
                       context.factory.keywords(context.version));
-    ref = context.ref.canonicalize(ref, context.where);
+
     if (std::optional root = context.ref.root(ref.root())) {
       return fetch_schema(context.rebind(ref.pointer().walk(*root), ref));
     }
@@ -286,12 +257,6 @@ private:
     return fetch_schema(context.rebind(ref.pointer().walk(*schema), ref));
   }
 
-  schema::Node const * resolve_dynamic(detail::Anchor const & ref) {
-    auto it = dynamic_anchors_.find(ref);
-    EXPECT_M(it != dynamic_anchors_.end(), "Unmatched $dynamicRef '" << ref << "'");
-    return it->second.reconstruct();
-  }
-
   template <Adapter A> schema::Node const * fetch_schema(detail::ParserContext<A> const & context) {
     // TODO(samjaffe): No longer promises uniqueness - instead track unique URI's
     if (std::optional cached = from_cache(context.where)) {
@@ -314,16 +279,16 @@ private:
     // Do this here first in order to protect from infinite loops
     alias(context.where, &it->second);
     it->second.construct(context);
-    if (not it->second.is_pure_reference()) {
-      return &it->second;
-    }
+    /* if (not it->second.is_pure_reference()) { */
+    return &it->second;
+    /* } */
 
     // Special Case - if the only is the reference constraint, then we don't need
     // to store it uniquely. Draft2019_09 supports directly extending a $ref schema
     // in the same schema, instead of requiring an allOf clause.
-    schema::Node const * node = *it->second.reference_schema();
-    cache_.erase(it);
-    return alias_cache_[context.where] = node;
+    /* schema::Node const * node = *it->second.reference_schema(); */
+    /* cache_.erase(it); */
+    /* return alias_cache_[context.where] = node; */
   }
 };
 }
@@ -350,18 +315,21 @@ template <Adapter A> detail::OnBlockExit Node::resolve_anchor(detail::ParserCont
     return nullptr;
   }
 
-  if (context.version == Version::Draft2019_09 && schema.contains("$recursiveAnchor")) {
-    EXPECT_M(schema["$recursiveAnchor"].as_boolean(), "$recursiveAnchor MUST be 'true'");
-
-    context.root.dynamic_anchor(detail::Anchor(), context);
-    return [&context]() { context.root.remove_dynamic_anchor(detail::Anchor(), context.where); };
+  if (context.version == schema::Version::Draft2019_09 && schema.contains("$recursiveAnchor") &&
+      schema["$recursiveAnchor"].as_boolean()) {
+    detail::Reference actual_location = context.ref.canonicalize({}, context.where);
+    if (context.root.dynamic_anchors_.emplace(detail::Anchor(), actual_location).second) {
+      return [&root = context.root]() { root.dynamic_anchors_.erase(detail::Anchor()); };
+    }
   }
 
   if (context.version > Version::Draft2019_09 && schema.contains("$dynamicAnchor")) {
     detail::Anchor anchor(schema["$dynamicAnchor"].as_string());
+    detail::Reference actual_location = context.ref.canonicalize({}, context.where);
 
-    context.root.dynamic_anchor(anchor, context);
-    return [&context, anchor]() { context.root.remove_dynamic_anchor(anchor, context.where); };
+    if (context.root.dynamic_anchors_.emplace(anchor, actual_location).second) {
+      return [&root = context.root, anchor]() { root.dynamic_anchors_.erase(anchor); };
+    }
   }
 
   return nullptr;
@@ -381,18 +349,19 @@ template <Adapter A> bool Node::resolve_reference(detail::ParserContext<A> const
     return false;
   }
 
-  if (context.version == Version::Draft2019_09 && schema.contains("$recursiveRef")) {
-    detail::Reference ref(schema["$recursiveRef"].as_string());
-    EXPECT_M(ref == detail::Reference(), "Only the root schema is permitted as a $recursiveRef");
+  std::string const dyn_ref =
+      context.version > schema::Version::Draft2019_09 ? "$dynamicRef" : "$recursiveRef";
+  if (schema.contains(dyn_ref)) {
+    detail::RootReference ref(schema[dyn_ref].as_string());
 
-    reference_ = context.root.resolve_dynamic(detail::Anchor());
-    return true;
-  }
-
-  if (context.version > Version::Draft2019_09 && schema.contains("$dynamicRef")) {
-    detail::Reference ref(schema["$dynamicRef"].as_string());
-
-    reference_ = context.root.resolve_dynamic(ref.anchor());
+    // TODO(samjaffe): Relocate...
+    if (auto it = context.root.dynamic_anchors_.find(ref.anchor());
+        it != context.root.dynamic_anchors_.end()) {
+      // TODO(samjaffe): This does not re-compute things...
+      reference_ = context.root.resolve(it->second, context);
+    } else {
+      reference_ = context.root.resolve(detail::Reference(ref), context);
+    }
     return true;
   }