Expand description
Frizbee is a SIMD typo-resistant fuzzy string matcher written in Rust, with bindings for C, C++, Python and WASM. The core of the algorithm uses Smith-Waterman with affine gaps, similar to FZF. In the included benchmark, with typo resistance disabled, it outperforms Nucleo by ~4x and FZF by ~5x and supports multithreading, see benchmarks. When matching against unicode, it outperforms Nucleo and FZF by 20x.
Used by blink.cmp, atuin, television, skim, and fff. Special thank you to stefanboca and ii14!
For commercial support, please contact me. I’d be happy to work with you directly! Also, please consider sponsoring me.
The core of the algorithm is Smith-Waterman with affine gaps and row-wise parallelism via SIMD. Besides the parallelism, this is the basis of other popular fuzzy matching algorithms like FZF and Nucleo. The main properties of Smith-Waterman are:
- Always finds the best alignment
- Supports insertion (unmatched char in haystack, basis of fuzzy matching)
- Supports deletion (unmatched char in needle, basis of typo-resistance)
- Supports substitution (haystack and needle char mismatch, basis of typo-resistance)
§Example: using Matcher
Matcher compiles the pattern once, allocates memory for the Smith Waterman
matrix, and reuses the selected SIMD backend.
Ideally, only construct these at most once per list. They’re cheap to construct, but end up being expensive if you construct them for each item in your list.
use frizbee::{Config, Matcher};
let needle = "fBr";
let haystacks = ["fooBar", "foo_bar", "barfoo", "prelude", "println!"];
let mut matcher = Matcher::new(needle, &Config::default());
let matches = matcher.match_list(&haystacks);
// or in parallel (8 threads, set to 0 to auto-detect)
let matches = matcher.match_list_parallel(&haystacks, 8);§Example: using multi-pattern queries
Matcher::from_query parses whitespace-separated atoms. Atom syntax can
control the matching mode:
fuzzy substring prefix suffix exact negated (combines with others)
foo 'foo ^foo foo$ ^foo$ !foouse frizbee::{Config, Matcher};
let haystacks = ["foo", "barfoo", "foobar", "bar/foo"];
let mut matcher = Matcher::from_query("foo !^bar", &Config::default());
let matches = matcher.match_list(&haystacks);Pattern::parse_query returns the parsed patterns, so per-pattern config
can be applied before building the matcher. For example, setting the max
typos based on needle length:
use frizbee::{Config, Matcher, Pattern};
let haystacks = ["foo", "barfoo", "foobar", "bar/foo"];
let patterns = Pattern::parse_query("foo !^bar")
.into_iter()
.map(|pattern| {
let max_typos = (pattern.needle.len() / 4) as u16;
pattern.max_typos(Some(max_typos))
})
.collect::<Vec<_>>();
let mut matcher = Matcher::from_patterns(&patterns, &Config::default());
let matches = matcher.match_list(&haystacks);§Example: using explicit Patterns
If query syntax is not a good fit, build patterns directly and pass them to
Matcher::from_patterns or Matcher::new (if you only have one pattern).
use frizbee::{Config, Matcher, Matching, Pattern, PatternConfig};
let patterns = [
Pattern::new("foo", PatternConfig::default()),
Pattern::new("bar", PatternConfig::default().matching(Some(Matching::Prefix))).negated(true),
];
let haystacks = ["foo", "barfoo", "foobar"];
let mut matcher = Matcher::from_patterns(&patterns, &Config::default());
let matches = matcher.match_list(&haystacks);§Example: using FuzzyMatchExt
The iterator API is convenient when chaining with other iterator adapters,
but it is slower than matching a full list with Matcher::match_list.
use frizbee::{iter::FuzzyMatchExt, Config, radix_sort_matches};
let haystacks = ["fooBar", "foo_bar", "prelude", "println!"];
let mut matches: Vec<_> = haystacks
.iter()
.fuzzy_match("fBr", &Config::default())
.collect();
radix_sort_matches(&mut matches);Modules§
- iter
- Iterator extension for fuzzy matching
- k_merge
- Merges multiple pre-sorted runs of
crate::Matches into a single sortedVecusing the k-way merge algorithm specialized forcrate::Matches.
Structs§
- Config
- Match
- Result of a fuzzy match, containing the score and index in the haystack
- Match
Indices - Like
Matchbut includes the indices of the chars in the haystack that matched the needle in reverse order - Matcher
- Primary entrypoint for fuzzy matching
- Pattern
- A single pattern to match, parsed from syntax like
!^foo - Pattern
Config - Per-pattern overrides for the matcher’s
Config. Every field is optional and falls back to the matcher’sConfigwhen left asNone(seePatternConfig::resolve) - Scoring
- Controls the scoring used by the smith waterman algorithm. Pay close attention to the documentation for each property, as small changes can lead to poor matching.
Enums§
- Case
Matching - Matching
- Selects the matching algorithm
- Sort
Strategy - Unicode
Matching
Functions§
- radix_
sort_ matches - Sorts a slice of
Matchvalues in-place by descendingscoreusing a stable radix sort. This assumes that the matches are already sorted by index.