Skip to content

Commit

Permalink
perf: Speedup reference resolving
Browse files Browse the repository at this point in the history
Signed-off-by: Dmitry Dygalo <[email protected]>
  • Loading branch information
Stranger6667 committed Oct 4, 2024
1 parent c9eac5c commit 7bff3d3
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 26 deletions.
2 changes: 1 addition & 1 deletion crates/jsonschema-referencing/src/anchors/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ impl Anchor {
)),
Anchor::Dynamic { name, resource, .. } => {
let mut last = resource;
for uri in resolver.dynamic_scope() {
for uri in &resolver.dynamic_scope() {
match resolver.registry.anchor(uri, name) {
Ok(anchor) => {
if let Anchor::Dynamic { resource, .. } = anchor {
Expand Down
2 changes: 2 additions & 0 deletions crates/jsonschema-referencing/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
mod anchors;
mod error;
mod list;
pub mod meta;
mod registry;
mod resolver;
Expand All @@ -11,6 +12,7 @@ pub mod uri;

pub(crate) use anchors::Anchor;
pub use error::{Error, UriError};
pub use list::List;
pub use registry::{Registry, RegistryOptions, SPECIFICATIONS};
pub use resolver::{Resolved, Resolver};
pub use resource::{Resource, ResourceRef};
Expand Down
82 changes: 82 additions & 0 deletions crates/jsonschema-referencing/src/list.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
use std::sync::Arc;

pub struct List<T> {
head: Option<Arc<Node<T>>>,
}

impl<T: Clone> Clone for List<T> {
fn clone(&self) -> Self {
List {
head: self.head.clone(),
}
}
}

impl<T> Drop for List<T> {
fn drop(&mut self) {
let mut current = self.head.take();
while let Some(node) = current {
if let Ok(mut node) = Arc::try_unwrap(node) {
current = node.next.take();
} else {
break;
}
}
}
}

impl<T> List<T> {
pub(crate) fn new() -> Self {
Self { head: None }
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.head.is_none()
}
#[must_use]
pub fn push_front(&self, value: T) -> Self {
List {
head: Some(Arc::new(Node {
value,
next: self.head.clone(),
})),
}
}
#[must_use]
pub fn iter(&self) -> Iter<'_, T> {
Iter {
current: self.head.as_ref(),
}
}
}

#[derive(Debug)]
pub(crate) struct Node<T> {
value: T,
next: Option<Arc<Node<T>>>,
}

#[derive(Debug)]
pub struct Iter<'a, T> {
current: Option<&'a Arc<Node<T>>>,
}

impl<'a, T> Iterator for Iter<'a, T> {
type Item = &'a T;

fn next(&mut self) -> Option<Self::Item> {
self.current.map(|current| {
let value = &current.value;
self.current = current.next.as_ref();
value
})
}
}

impl<'a, T> IntoIterator for &'a List<T> {
type IntoIter = Iter<'a, T>;
type Item = &'a T;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
3 changes: 2 additions & 1 deletion crates/jsonschema-referencing/src/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use serde_json::Value;

use crate::{
anchors::{AnchorKey, AnchorKeyRef},
list::List,
meta, uri, Anchor, DefaultRetriever, Draft, Error, Resolver, Resource, Retrieve,
};

Expand Down Expand Up @@ -228,7 +229,7 @@ impl Registry {
pub fn resolver_from_raw_parts(
&self,
base_uri: Uri<String>,
scopes: VecDeque<Uri<String>>,
scopes: List<Uri<String>>,
) -> Resolver {
Resolver::from_parts(self, base_uri, scopes)
}
Expand Down
19 changes: 8 additions & 11 deletions crates/jsonschema-referencing/src/resolver.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
use core::fmt;
use std::collections::VecDeque;

use fluent_uri::Uri;
use serde_json::Value;

use crate::{uri, Draft, Error, Registry, ResourceRef};
use crate::{list::List, uri, Draft, Error, Registry, ResourceRef};

/// A reference resolver.
///
Expand All @@ -13,7 +12,7 @@ use crate::{uri, Draft, Error, Registry, ResourceRef};
pub struct Resolver<'r> {
pub(crate) registry: &'r Registry,
base_uri: Uri<String>,
scopes: VecDeque<Uri<String>>,
scopes: List<Uri<String>>,
}

impl<'r> PartialEq for Resolver<'r> {
Expand Down Expand Up @@ -50,13 +49,13 @@ impl<'r> Resolver<'r> {
Self {
registry,
base_uri,
scopes: VecDeque::new(),
scopes: List::new(),
}
}
pub(crate) fn from_parts(
registry: &'r Registry,
base_uri: Uri<String>,
scopes: VecDeque<Uri<String>>,
scopes: List<Uri<String>>,
) -> Self {
Self {
registry,
Expand Down Expand Up @@ -126,7 +125,7 @@ impl<'r> Resolver<'r> {
.and_then(Value::as_bool)
.unwrap_or(false)
{
for uri in self.dynamic_scope() {
for uri in &self.dynamic_scope() {
let next_resolved = self.lookup(uri.as_str())?;

match next_resolved.contents {
Expand Down Expand Up @@ -167,19 +166,17 @@ impl<'r> Resolver<'r> {
}
}
#[must_use]
pub fn dynamic_scope(&self) -> impl ExactSizeIterator<Item = &Uri<String>> {
self.scopes.iter()
pub fn dynamic_scope(&self) -> List<Uri<String>> {
self.scopes.clone()
}
fn evolve(&self, base_uri: Uri<String>) -> Resolver<'r> {
if !self.base_uri.as_str().is_empty()
&& (self.scopes.is_empty() || base_uri != self.base_uri)
{
let mut scopes = self.scopes.clone();
scopes.push_front(self.base_uri.clone());
Resolver {
registry: self.registry,
base_uri,
scopes,
scopes: self.scopes.push_front(self.base_uri.clone()),
}
} else {
Resolver {
Expand Down
17 changes: 7 additions & 10 deletions crates/jsonschema/src/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ use crate::{
use ahash::{AHashMap, AHashSet};
use once_cell::sync::Lazy;
use referencing::{
uri, Draft, Registry, Resolved, Resolver, Resource, ResourceRef, Retrieve, Uri, SPECIFICATIONS,
uri, Draft, List, Registry, Resolved, Resolver, Resource, ResourceRef, Retrieve, Uri,
SPECIFICATIONS,
};
use serde_json::Value;
use std::{cell::RefCell, collections::VecDeque, rc::Rc, sync::Arc};
use std::{cell::RefCell, rc::Rc, sync::Arc};

const DEFAULT_SCHEME: &str = "json-schema";
pub(crate) const DEFAULT_ROOT_URL: &str = "json-schema:///";
Expand Down Expand Up @@ -101,8 +102,8 @@ impl<'a> Context<'a> {
self.resolver.lookup(reference)
}

pub(crate) fn scopes(&self) -> VecDeque<Uri<String>> {
VecDeque::from_iter(self.resolver.dynamic_scope().cloned())
pub(crate) fn scopes(&self) -> List<Uri<String>> {
self.resolver.dynamic_scope()
}

/// Create a JSON Pointer from the current `schema_path` & a new chunk.
Expand Down Expand Up @@ -203,7 +204,7 @@ impl<'a> Context<'a> {
&self,
reference: &str,
is_recursive: bool,
) -> Result<Option<(BaseUri, VecDeque<BaseUri>, Resource)>, ValidationError<'static>> {
) -> Result<Option<(BaseUri, List<BaseUri>, Resource)>, ValidationError<'static>> {
let resolved = if reference == "#" {
// Known & simple recursive reference
// It may also use some additional logic from the `$recursiveAnchor` keyword
Expand All @@ -220,11 +221,7 @@ impl<'a> Context<'a> {
};
let resource = self.draft().create_resource(resolved.contents().clone());
let mut base_uri = resolved.resolver().base_uri().to_owned();
let scopes = resolved
.resolver()
.dynamic_scope()
.cloned()
.collect::<VecDeque<_>>();
let scopes = resolved.resolver().dynamic_scope();
if let Some(id) = resource.id() {
base_uri = uri::resolve_against(&base_uri.borrow(), id)?;
};
Expand Down
6 changes: 3 additions & 3 deletions crates/jsonschema/src/keywords/ref_.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{collections::VecDeque, rc::Rc, sync::Arc};
use std::{rc::Rc, sync::Arc};

use crate::{
compiler,
Expand All @@ -11,7 +11,7 @@ use crate::{
ValidationError, ValidationOptions,
};
use once_cell::sync::OnceCell;
use referencing::{uri, Draft, Registry, Resource, Uri};
use referencing::{uri, Draft, List, Registry, Resource, Uri};
use serde_json::{Map, Value};

pub(crate) enum RefValidator {
Expand Down Expand Up @@ -61,7 +61,7 @@ pub(crate) struct LazyRefValidator {
resource: Resource,
config: Arc<ValidationOptions>,
registry: Arc<Registry>,
scopes: VecDeque<Uri<String>>,
scopes: List<Uri<String>>,
base_uri: Uri<String>,
draft: Draft,
inner: OnceCell<SchemaNode>,
Expand Down

0 comments on commit 7bff3d3

Please sign in to comment.