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

Fix root paths candidates #331

Merged
merged 2 commits into from
Nov 20, 2023
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
101 changes: 81 additions & 20 deletions stack-graphs/src/stitching.rs
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,12 @@ pub struct Database {
symbol_stack_keys: ListArena<Handle<Symbol>>,
symbol_stack_key_cache: HashMap<SymbolStackCacheKey, SymbolStackKeyHandle>,
paths_by_start_node: SupplementalArena<Node, Vec<Handle<PartialPath>>>,
root_paths_by_precondition: SupplementalArena<SymbolStackKeyCell, Vec<Handle<PartialPath>>>,
root_paths_by_precondition_prefix:
SupplementalArena<SymbolStackKeyCell, Vec<Handle<PartialPath>>>,
root_paths_by_precondition_with_variable:
SupplementalArena<SymbolStackKeyCell, Vec<Handle<PartialPath>>>,
root_paths_by_precondition_without_variable:
SupplementalArena<SymbolStackKeyCell, Vec<Handle<PartialPath>>>,
incoming_paths: SupplementalArena<Node, Degree>,
}

Expand All @@ -297,7 +302,9 @@ impl Database {
symbol_stack_keys: List::new_arena(),
symbol_stack_key_cache: HashMap::new(),
paths_by_start_node: SupplementalArena::new(),
root_paths_by_precondition: SupplementalArena::new(),
root_paths_by_precondition_prefix: SupplementalArena::new(),
root_paths_by_precondition_with_variable: SupplementalArena::new(),
root_paths_by_precondition_without_variable: SupplementalArena::new(),
incoming_paths: SupplementalArena::new(),
}
}
Expand All @@ -311,7 +318,9 @@ impl Database {
self.symbol_stack_keys.clear();
self.symbol_stack_key_cache.clear();
self.paths_by_start_node.clear();
self.root_paths_by_precondition.clear();
self.root_paths_by_precondition_prefix.clear();
self.root_paths_by_precondition_with_variable.clear();
self.root_paths_by_precondition_without_variable.clear();
self.incoming_paths.clear();
}

Expand Down Expand Up @@ -341,14 +350,21 @@ impl Database {
if graph[start_node].is_root() {
// The join node is root, so there's no need to use half-open symbol stacks here, as we
// do for [`PartialPath::concatenate`][].
let key = SymbolStackKey::from_partial_symbol_stack(
let mut key = SymbolStackKey::from_partial_symbol_stack(
partials,
self,
symbol_stack_precondition,
);
if !key.is_empty() {
let key_handle = key.back_handle();
self.root_paths_by_precondition[key_handle].push(handle);
match symbol_stack_precondition.has_variable() {
true => self.root_paths_by_precondition_with_variable[key.back_handle()]
.push(handle),
false => self.root_paths_by_precondition_without_variable[key.back_handle()]
.push(handle),
}
}
while key.pop_back(self).is_some() && !key.is_empty() {
self.root_paths_by_precondition_prefix[key.back_handle()].push(handle);
}
} else {
// Otherwise index it by its source node.
Expand All @@ -374,12 +390,12 @@ impl Database {
if graph[path.end_node].is_root() {
// The join node is root, so there's no need to use half-open symbol stacks here, as we
// do for [`PartialPath::concatenate`][].
let key = SymbolStackKey::from_partial_symbol_stack(
self.find_candidate_partial_paths_from_root(
graph,
partials,
self,
path.symbol_stack_postcondition,
Some(path.symbol_stack_postcondition),
result,
);
self.find_candidate_partial_paths_from_root(graph, partials, Some(key), result);
} else {
self.find_candidate_partial_paths_from_node(graph, partials, path.end_node, result);
}
Expand All @@ -392,39 +408,84 @@ impl Database {
&mut self,
graph: &StackGraph,
partials: &mut PartialPaths,
symbol_stack: Option<SymbolStackKey>,
symbol_stack: Option<PartialSymbolStack>,
result: &mut R,
) where
R: std::iter::Extend<Handle<PartialPath>>,
{
// If the path currently ends at the root node, then we need to look up partial paths whose
// symbol stack precondition is compatible with the path.
match symbol_stack {
Some(mut symbol_stack) => loop {
Some(symbol_stack) => {
let mut key =
SymbolStackKey::from_partial_symbol_stack(partials, self, symbol_stack);
copious_debugging!(
" Search for symbol stack <{}>",
symbol_stack.display(graph, self)
key.display(graph, self)
);
let key_handle = symbol_stack.back_handle();
if let Some(paths) = self.root_paths_by_precondition.get(key_handle) {
// paths that have exactly this symbol stack
if let Some(paths) = self
.root_paths_by_precondition_without_variable
.get(key.back_handle())
{
#[cfg(feature = "copious-debugging")]
{
for path in paths {
copious_debugging!(
" Found path {}",
" Found path with exact stack {}",
self[*path].display(graph, partials)
);
}
}
result.extend(paths.iter().copied());
}
if symbol_stack.pop_back(self).is_none() {
break;
// paths that have an extension of this symbol stack
if symbol_stack.has_variable() {
if let Some(paths) = self
.root_paths_by_precondition_prefix
.get(key.back_handle())
{
#[cfg(feature = "copious-debugging")]
{
for path in paths {
copious_debugging!(
" Found path with smaller stack {}",
self[*path].display(graph, partials)
);
}
}
result.extend(paths.iter().copied());
}
}
loop {
// paths that have a prefix of this symbol stack
if let Some(paths) = self
.root_paths_by_precondition_with_variable
.get(key.back_handle())
{
#[cfg(feature = "copious-debugging")]
{
for path in paths {
copious_debugging!(
" Found path with smaller stack {}",
self[*path].display(graph, partials)
);
}
}
result.extend(paths.iter().copied());
}
if key.pop_back(self).is_none() {
break;
}
}
},
}
None => {
copious_debugging!(" Search for all root paths");
for (_, paths) in self.root_paths_by_precondition.iter() {
for (_, paths) in self
.root_paths_by_precondition_with_variable
.iter()
.chain(self.root_paths_by_precondition_without_variable.iter())
{
#[cfg(feature = "copious-debugging")]
{
for path in paths {
Expand Down
69 changes: 44 additions & 25 deletions stack-graphs/src/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

use bincode::error::DecodeError;
use bincode::error::EncodeError;
use itertools::Itertools;
use rusqlite::functions::FunctionFlags;
use rusqlite::types::ValueRef;
use rusqlite::Connection;
Expand All @@ -33,7 +34,7 @@ use crate::stitching::ForwardCandidates;
use crate::CancellationError;
use crate::CancellationFlag;

const VERSION: usize = 5;
const VERSION: usize = 6;

const SCHEMA: &str = r#"
CREATE TABLE metadata (
Expand Down Expand Up @@ -616,9 +617,12 @@ impl SQLiteReader {
" * Load extensions from root with symbol stack {}",
symbol_stack.display(&self.graph, &mut self.partials)
);
let symbol_stack_prefixes =
symbol_stack.storage_key_prefixes(&self.graph, &mut self.partials);
for symbol_stack in symbol_stack_prefixes {
let mut stmt = self.conn.prepare_cached(
"SELECT file,value from root_paths WHERE symbol_stack LIKE ? ESCAPE ?",
)?;
let (symbol_stack_patterns, escape) =
symbol_stack.storage_key_patterns(&self.graph, &mut self.partials);
for symbol_stack in symbol_stack_patterns {
copious_debugging!(
" * Load extensions from root with prefix symbol stack {}",
symbol_stack
Expand All @@ -627,10 +631,7 @@ impl SQLiteReader {
copious_debugging!(" > Already loaded");
continue;
}
let mut stmt = self
.conn
.prepare_cached("SELECT file,value from root_paths WHERE symbol_stack = ?")?;
let paths = stmt.query_map([symbol_stack], |row| {
let paths = stmt.query_map([symbol_stack, escape.clone()], |row| {
let file = row.get::<_, String>(0)?;
let value = row.get::<_, Vec<u8>>(1)?;
Ok((file, value))
Expand Down Expand Up @@ -682,41 +683,59 @@ impl SQLiteReader {
}

/// Get the stack graph, partial paths arena, and path database for the currently loaded data.
pub fn get(&mut self) -> (&StackGraph, &mut PartialPaths, &mut Database) {
(&self.graph, &mut self.partials, &mut self.db)
pub fn get(&mut self) -> (&mut StackGraph, &mut PartialPaths, &mut Database) {
(&mut self.graph, &mut self.partials, &mut self.db)
}
}

// Methods for computing keys and patterns for a symbol stack. The format of a storage key is:
//
// has-var GS ( symbol (US symbol)* )?
//
// where has-var is "V" if the symbol stack has a variable, "X" otherwise.
impl PartialSymbolStack {
/// Returns a string representation of this symbol stack for indexing in the database.
fn storage_key(mut self, graph: &StackGraph, partials: &mut PartialPaths) -> String {
fn storage_key(self, graph: &StackGraph, partials: &mut PartialPaths) -> String {
let mut key = String::new();
while let Some(symbol) = self.pop_front(partials) {
if !key.is_empty() {
key += "\u{241F}";
}
key += &graph[symbol.symbol];
match self.has_variable() {
true => key += "V\u{241E}",
false => key += "X\u{241E}",
}
key += &self
.iter(partials)
.map(|s| &graph[s.symbol])
.join("\u{241F}");
key
}

/// Returns string representations for all prefixes of this symbol stack for querying the
/// index in the database.
fn storage_key_prefixes(
fn storage_key_patterns(
mut self,
graph: &StackGraph,
partials: &mut PartialPaths,
) -> Vec<String> {
let mut key_prefixes = vec![String::new()];
) -> (Vec<String>, String) {
let mut key_patterns = Vec::new();
let mut symbols = String::new();
while let Some(symbol) = self.pop_front(partials) {
let mut key = key_prefixes.last().unwrap().to_string();
if !key.is_empty() {
key += "\u{241F}";
if !symbols.is_empty() {
symbols += "\u{241F}";
}
key += &graph[symbol.symbol];
key_prefixes.push(key);
let symbol = graph[symbol.symbol]
.replace("%", "\\%")
.replace("_", "\\_")
.to_string();
symbols += &symbol;
// patterns for paths matching a prefix of this stack
key_patterns.push("V\u{241E}".to_string() + &symbols);
}
// pattern for paths matching exactly this stack
key_patterns.push("X\u{241E}".to_string() + &symbols);
if self.has_variable() {
// patterns for paths for which this stack is a prefix
key_patterns.push("_\u{241E}".to_string() + &symbols + "\u{241F}%");
}
key_prefixes
(key_patterns, "\\".to_string())
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ use stack_graphs::partial::PartialScopedSymbol;
use stack_graphs::partial::PartialSymbolStack;
use stack_graphs::stitching::Database;
use stack_graphs::stitching::ForwardPartialPathStitcher;
use stack_graphs::stitching::SymbolStackKey;
use stack_graphs::NoCancellation;

use crate::test_graphs;
Expand Down Expand Up @@ -53,8 +52,12 @@ fn check_root_partial_paths(
}

let mut results = Vec::<Handle<PartialPath>>::new();
let key = SymbolStackKey::from_partial_symbol_stack(&mut partials, &mut db, symbol_stack);
db.find_candidate_partial_paths_from_root(graph, &mut partials, Some(key), &mut results);
db.find_candidate_partial_paths_from_root(
graph,
&mut partials,
Some(symbol_stack),
&mut results,
);

let actual_partial_paths = results
.into_iter()
Expand Down
3 changes: 3 additions & 0 deletions stack-graphs/tests/it/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,7 @@ mod graph;
mod partial;
#[cfg(feature = "serde")]
mod serde;
mod stitching;
#[cfg(feature = "storage")]
mod storage;
mod util;
Loading