Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add feature 'multithread' to use Rsonpath in multithread context #622

Merged
merged 3 commits into from
Dec 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/rsonpath-lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,4 @@ path = "examples/approx_spans_usage.rs"
doc-scrape-examples = true

[lints]
workspace = true
workspace = true
10 changes: 5 additions & 5 deletions crates/rsonpath-lib/src/automaton.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,20 @@ use crate::{automaton::error::CompilerError, debug, string_pattern::StringPatter
use nfa::NondeterministicAutomaton;
use rsonpath_syntax::{num::JsonUInt, JsonPathQuery};
use smallvec::SmallVec;
use std::{fmt::Display, ops::Index, rc::Rc};
use std::{fmt::Display, ops::Index, sync::Arc};

/// A minimal, deterministic automaton representing a JSONPath query.
#[derive(Debug, PartialEq, Eq)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Automaton {
states: Vec<StateTable>,
}

/// Transition when a JSON member name matches a [`StringPattern`].
pub type MemberTransition = (Rc<StringPattern>, State);
pub type MemberTransition = (Arc<StringPattern>, State);

/// Transition on elements of an array with indices specified by either a single index
/// or a simple slice expression.
#[derive(Debug, PartialEq, Eq)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ArrayTransition {
label: ArrayTransitionLabel,
target: State,
Expand All @@ -44,7 +44,7 @@ pub(super) enum ArrayTransitionLabel {
///
/// Contains transitions triggered by matching member names or array indices, and a fallback transition
/// triggered when none of the labelled transitions match.
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct StateTable {
attributes: StateAttributes,
member_transitions: SmallVec<[MemberTransition; 2]>,
Expand Down
44 changes: 22 additions & 22 deletions crates/rsonpath-lib/src/automaton/minimizer.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Determinization and minimization of an NFA into the final DFA used by the engines.

use std::rc::Rc;
use std::sync::Arc;

// NOTE: Some comments in this module are outdated, because the minimizer doesn't
// actually produce minimal automata as of now - see #91.
Expand Down Expand Up @@ -50,7 +50,7 @@ pub(super) struct Minimizer {
#[derive(Debug)]
struct SuperstateTransitionTable {
array: ArrayTransitionSet,
member: VecMap<Rc<StringPattern>, SmallSet256>,
member: VecMap<Arc<StringPattern>, SmallSet256>,
wildcard: SmallSet256,
}

Expand Down Expand Up @@ -177,7 +177,7 @@ impl Minimizer {
&self,
id: DfaStateId,
array_transitions: &[ArrayTransition],
member_transitions: &[(Rc<StringPattern>, DfaStateId)],
member_transitions: &[(Arc<StringPattern>, DfaStateId)],
fallback: DfaStateId,
) -> StateAttributes {
let mut attrs = StateAttributesBuilder::new();
Expand Down Expand Up @@ -554,8 +554,8 @@ mod tests {
#[test]
fn interstitial_descendant_wildcard() {
// Query = $..a.b..*.a..b
let label_a = Rc::new(StringPattern::new(&JsonString::new("a")));
let label_b = Rc::new(StringPattern::new(&JsonString::new("b")));
let label_a = Arc::new(StringPattern::new(&JsonString::new("a")));
let label_b = Arc::new(StringPattern::new(&JsonString::new("b")));

let nfa = NondeterministicAutomaton {
ordered_states: vec![
Expand Down Expand Up @@ -623,8 +623,8 @@ mod tests {
#[test]
fn interstitial_nondescendant_wildcard() {
// Query = $..a.b.*.a..b
let label_a = Rc::new(StringPattern::new(&JsonString::new("a")));
let label_b = Rc::new(StringPattern::new(&JsonString::new("b")));
let label_a = Arc::new(StringPattern::new(&JsonString::new("a")));
let label_b = Arc::new(StringPattern::new(&JsonString::new("b")));

let nfa = NondeterministicAutomaton {
ordered_states: vec![
Expand Down Expand Up @@ -698,7 +698,7 @@ mod tests {
#[test]
fn simple_multi_accepting() {
// Query = $..a.*
let label = Rc::new(StringPattern::new(&JsonString::new("a")));
let label = Arc::new(StringPattern::new(&JsonString::new("a")));

let nfa = NondeterministicAutomaton {
ordered_states: vec![
Expand Down Expand Up @@ -796,7 +796,7 @@ mod tests {
#[test]
fn chained_wildcard_children() {
// Query = $.a.*.*.*
let label = Rc::new(StringPattern::new(&JsonString::new("a")));
let label = Arc::new(StringPattern::new(&JsonString::new("a")));

let nfa = NondeterministicAutomaton {
ordered_states: vec![
Expand Down Expand Up @@ -857,7 +857,7 @@ mod tests {
#[test]
fn chained_wildcard_children_after_descendant() {
// Query = $..a.*.*
let label = Rc::new(StringPattern::new(&JsonString::new("a")));
let label = Arc::new(StringPattern::new(&JsonString::new("a")));

let nfa = NondeterministicAutomaton {
ordered_states: vec![
Expand Down Expand Up @@ -935,11 +935,11 @@ mod tests {
#[test]
fn child_and_descendant() {
// Query = $.x..a.b.a.b.c..d
let label_a = Rc::new(StringPattern::new(&JsonString::new("a")));
let label_b = Rc::new(StringPattern::new(&JsonString::new("b")));
let label_c = Rc::new(StringPattern::new(&JsonString::new("c")));
let label_d = Rc::new(StringPattern::new(&JsonString::new("d")));
let label_x = Rc::new(StringPattern::new(&JsonString::new("x")));
let label_a = Arc::new(StringPattern::new(&JsonString::new("a")));
let label_b = Arc::new(StringPattern::new(&JsonString::new("b")));
let label_c = Arc::new(StringPattern::new(&JsonString::new("c")));
let label_d = Arc::new(StringPattern::new(&JsonString::new("d")));
let label_x = Arc::new(StringPattern::new(&JsonString::new("x")));

let nfa = NondeterministicAutomaton {
ordered_states: vec![
Expand Down Expand Up @@ -1021,9 +1021,9 @@ mod tests {
#[test]
fn child_descendant_and_child_wildcard() {
// Query = $.x.*..a.*.b
let label_a = Rc::new(StringPattern::new(&JsonString::new("a")));
let label_b = Rc::new(StringPattern::new(&JsonString::new("b")));
let label_x = Rc::new(StringPattern::new(&JsonString::new("x")));
let label_a = Arc::new(StringPattern::new(&JsonString::new("a")));
let label_b = Arc::new(StringPattern::new(&JsonString::new("b")));
let label_x = Arc::new(StringPattern::new(&JsonString::new("x")));

let nfa = NondeterministicAutomaton {
ordered_states: vec![
Expand Down Expand Up @@ -1103,10 +1103,10 @@ mod tests {
#[test]
fn all_name_and_wildcard_selectors() {
// Query = $.a.b..c..d.*..*
let label_a = Rc::new(StringPattern::new(&JsonString::new("a")));
let label_b = Rc::new(StringPattern::new(&JsonString::new("b")));
let label_c = Rc::new(StringPattern::new(&JsonString::new("c")));
let label_d = Rc::new(StringPattern::new(&JsonString::new("d")));
let label_a = Arc::new(StringPattern::new(&JsonString::new("a")));
let label_b = Arc::new(StringPattern::new(&JsonString::new("b")));
let label_c = Arc::new(StringPattern::new(&JsonString::new("c")));
let label_d = Arc::new(StringPattern::new(&JsonString::new("d")));

let nfa = NondeterministicAutomaton {
ordered_states: vec![
Expand Down
16 changes: 8 additions & 8 deletions crates/rsonpath-lib/src/automaton/nfa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::{automaton::SimpleSlice, error::UnsupportedFeatureError, string_patte

use super::{error::CompilerError, ArrayTransitionLabel};
use rsonpath_syntax::{str::JsonString, JsonPathQuery, Step};
use std::{collections::HashMap, fmt::Display, ops::Index, rc::Rc};
use std::{collections::HashMap, fmt::Display, ops::Index, sync::Arc};

/// An NFA representing a query. It is always a directed path
/// from an initial state to the unique accepting state at the end,
Expand Down Expand Up @@ -34,7 +34,7 @@ pub(super) enum Transition {
/// A transition matching array indices.
Array(ArrayTransitionLabel),
/// A transition matching a specific member.
Member(Rc<StringPattern>),
Member(Arc<StringPattern>),
/// A transition matching anything.
Wildcard,
}
Expand Down Expand Up @@ -71,7 +71,7 @@ impl NondeterministicAutomaton {
use rsonpath_syntax::{Index, Selector};
use std::collections::hash_map::Entry;

let mut string_pattern_cache: HashMap<&'q JsonString, Rc<StringPattern>> = HashMap::new();
let mut string_pattern_cache: HashMap<&'q JsonString, Arc<StringPattern>> = HashMap::new();

let states_result: Result<Vec<NfaState>, CompilerError> = query
.segments()
Expand All @@ -87,7 +87,7 @@ impl NondeterministicAutomaton {
let pattern = match string_pattern_cache.entry(name) {
Entry::Occupied(pat) => pat.get().clone(),
Entry::Vacant(entry) => {
let pat = Rc::new(StringPattern::new(name));
let pat = Arc::new(StringPattern::new(name));
entry.insert(pat.clone());
pat
}
Expand Down Expand Up @@ -242,10 +242,10 @@ mod tests {

let expected_automaton = NondeterministicAutomaton {
ordered_states: vec![
NfaState::Direct(Transition::Member(Rc::new(StringPattern::new(&label_a)))),
NfaState::Direct(Transition::Member(Rc::new(StringPattern::new(&label_b)))),
NfaState::Recursive(Transition::Member(Rc::new(StringPattern::new(&label_c)))),
NfaState::Recursive(Transition::Member(Rc::new(StringPattern::new(&label_d)))),
NfaState::Direct(Transition::Member(Arc::new(StringPattern::new(&label_a)))),
NfaState::Direct(Transition::Member(Arc::new(StringPattern::new(&label_b)))),
NfaState::Recursive(Transition::Member(Arc::new(StringPattern::new(&label_c)))),
NfaState::Recursive(Transition::Member(Arc::new(StringPattern::new(&label_d)))),
NfaState::Direct(Transition::Wildcard),
NfaState::Direct(Transition::Wildcard),
NfaState::Recursive(Transition::Wildcard),
Expand Down
5 changes: 3 additions & 2 deletions crates/rsonpath-lib/src/engine/head_skipping.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
//! Engine decorator that performs **head skipping** &ndash; an extremely optimized search for
//! the first matching member name in a query starting with a self-looping state.
//! This happens in queries starting with a descendant selector.
use std::rc::Rc;

use std::sync::Arc;

use crate::{
automaton::{Automaton, State},
Expand Down Expand Up @@ -67,7 +68,7 @@ pub(super) struct HeadSkip<'b, I, V, const N: usize> {
bytes: &'b I,
state: State,
is_accepting: bool,
member_name: Rc<StringPattern>,
member_name: Arc<StringPattern>,
simd: V,
}

Expand Down
5 changes: 4 additions & 1 deletion crates/rsonpath-lib/src/engine/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,15 @@ use smallvec::{smallvec, SmallVec};
///
/// The engine is stateless, meaning that it can be executed
/// on any number of separate inputs, even on separate threads.
#[derive(Debug)]

#[derive(Clone, Debug)]
pub struct MainEngine {
automaton: Automaton,
simd: SimdConfiguration,
}

static_assertions::assert_impl_all!(MainEngine: Send, Sync);

impl Compiler for MainEngine {
type E = Self;

Expand Down
Loading