Skip to content

Commit

Permalink
Efficient prefix matching, remove dep.
Browse files Browse the repository at this point in the history
  • Loading branch information
WINSDK committed Apr 25, 2024
1 parent 7c4ef57 commit f1ed0f0
Show file tree
Hide file tree
Showing 7 changed files with 187 additions and 147 deletions.
26 changes: 0 additions & 26 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 11 additions & 4 deletions commands/src/debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
// TODO: Implement binary presidence (10 + 10 * 10 == 110).
// This likely requires parsing in two steps where we first generate tokens.

use std::fmt;
use debugvault::Index;
use std::fmt;

const MAX_DEPTH: usize = 256;

Expand Down Expand Up @@ -532,9 +532,16 @@ impl CompleteExpr {
}
}

pub fn autocomplete(&self, index: &Index, cursor: usize) -> Option<(Vec<String>, Span)> {
self.find_matching_symbol(&self.root, cursor)
.map(|(prefix, span)| (index.prefix_match_func(prefix), span))
pub fn autocomplete<'a>(
&self,
index: &'a Index,
cursor: usize,
) -> Option<(debugvault::prefix::Match, Span)> {
if let Some((prefix, span)) = self.find_matching_symbol(&self.root, cursor) {
return Some((index.prefixes.find(prefix), span));
}

None
}

pub fn parse(s: &str) -> Result<Self, Error> {
Expand Down
4 changes: 2 additions & 2 deletions commands/src/gui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -330,9 +330,9 @@ impl<'src> Context<'src> {
if let Some((suggestions, span)) = expr.autocomplete(self.index, relative_cursor) {
let span = span.start() + offset..span.end() + offset;

for suggestion in suggestions {
for suggestion in suggestions.iter(&self.index.prefixes) {
let mut src = self.src.to_string();
src.replace_range(span.clone(), &suggestion);
src.replace_range(span.clone(), suggestion.as_str());
self.suggestions.push(src);
}
}
Expand Down
1 change: 0 additions & 1 deletion debugvault/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ config = { path = "../config" }
tokenizing = { path = "../tokenizing" }
processor_shared = { path = "../processor_shared" }
bitflags = "2"
radix_trie = "0.2"
gimli = { workspace = true }
pdb = { workspace = true }
object = { workspace = true }
Expand Down
74 changes: 0 additions & 74 deletions debugvault/src/common.rs

This file was deleted.

95 changes: 55 additions & 40 deletions debugvault/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
use binformat::RawSymbol;
use common::*;
use demangler::TokenStream;
use dwarf::Dwarf;
use processor_shared::{AddressMap, Addressed};
use radix_trie::{Trie, TrieCommon};
use std::path::Path;
use std::sync::Arc;
use std::fmt;
use tokenizing::Token;

mod common;
pub mod prefix;
mod demangler;
mod dwarf;
mod error;
Expand Down Expand Up @@ -37,7 +35,7 @@ pub struct FileAttr {

pub struct Symbol {
name: TokenStream,
name_as_str: ArcStr,
name_as_str: Arc<str>,
module: Option<String>,
is_intrinsics: bool,
}
Expand Down Expand Up @@ -66,7 +64,7 @@ impl Default for Symbol {
fn default() -> Self {
Self {
name: TokenStream::new(""),
name_as_str: ArcStr::new(""),
name_as_str: Arc::from(""),
module: None,
is_intrinsics: false,
}
Expand All @@ -84,6 +82,7 @@ impl Symbol {
self.module.as_deref()
}

#[inline]
pub fn as_str(&self) -> &str {
&self.name_as_str
}
Expand Down Expand Up @@ -121,8 +120,8 @@ pub struct Index {
/// The addresses are sorted.
pub file_attrs: AddressMap<FileAttr>,

/// Prefix tree for finding symbols.
trie: Trie<ArcStr, Arc<Symbol>>,
/// Efficient string match searcher.
pub prefixes: prefix::PrefixMatcher,

/// Number of named compiler artifacts.
named_len: usize,
Expand Down Expand Up @@ -176,7 +175,7 @@ impl Index {
let demangled = demangler::parse(item.name);
let is_intrinsics = is_name_an_intrinsic(item.name);
let name_as_str = String::from_iter(demangled.tokens().iter().map(|t| &t.text[..]));
let name_as_str = ArcStr::new(&name_as_str);
let name_as_str = Arc::from(name_as_str);
let symbol = Symbol {
name_as_str,
name: demangled,
Expand Down Expand Up @@ -231,13 +230,16 @@ impl Index {
}

fn build_prefix_tree(&mut self) {
log::PROGRESS.set("Building prefix tree", self.syms.len());
log::PROGRESS.set("Building prefix tree", self.syms.len() + 1);

// Radix-prefix tree for fast lookups.
for Addressed { item: func, .. } in self.syms.iter() {
self.trie.insert(func.name_as_str.clone(), Arc::clone(func));
self.prefixes.insert(func);
log::PROGRESS.step();
}

self.prefixes.reorder();
log::PROGRESS.step();
}

pub fn named_funcs_count(&self) -> usize {
Expand Down Expand Up @@ -273,41 +275,12 @@ impl Index {
addr,
item: Arc::new(Symbol {
name: TokenStream::simple(name),
name_as_str: ArcStr::new(name),
name_as_str: Arc::from(name),
module: None,
is_intrinsics: false,
}),
})
}

pub fn prefix_match_func(&self, prefix: &str) -> Vec<String> {
let arc_prefix = ArcStr::new(prefix);
let desc = match self.trie.get_raw_descendant(&arc_prefix) {
Some(desc) => desc.keys().collect(),
None => Vec::new(),
};

sort_by_shortest_match(&desc, prefix)
}
}

/// Sort the first 100 strings by length if they have a matching prefix.
fn sort_by_shortest_match(input: &[&ArcStr], prefix: &str) -> Vec<String> {
let mut matches: Vec<String> = Vec::new();

for possible in input {
if matches.len() == 100 {
break;
}

if possible.starts_with(prefix) {
matches.push(possible.to_string());
}
}

// sort the matches by length
matches.sort_by_key(|a| a.len());
matches
}

#[cfg(target_os = "macos")]
Expand Down Expand Up @@ -356,3 +329,45 @@ pub fn macho_dwarf(obj: &object::File, path: &Path) -> Result<Dwarf, dwarf::Erro

Ok(dwarf)
}

pub fn parallel_compute<In, Out, F>(items: Vec<In>, output: &mut Vec<Out>, transformer: F)
where
F: FnOnce(&In) -> Out,
F: Send + Copy,
In: Sync,
Out: Send + Sync,
{
let thread_count = std::thread::available_parallelism().unwrap().get();

// For small item counts, perform single-threaded.
if items.len() < thread_count {
for item in items.iter() {
output.push(transformer(item));
}

return;
}

// Multithreaded.
std::thread::scope(|s| {
let chunks = items.chunks(items.len() / thread_count);
let mut threads = Vec::with_capacity(thread_count);

for chunk in chunks {
let thread = s.spawn(move || {
let mut result = Vec::with_capacity(chunk.len());
for item in chunk {
result.push(transformer(item));
}
result
});

threads.push(thread);
}

for thread in threads {
let chunk = thread.join().unwrap();
output.extend(chunk);
}
});
}

0 comments on commit f1ed0f0

Please sign in to comment.