pub struct FoldedText { /* private fields */ }Expand description
A haystack folded once, ready to be searched many times.
§Why this exists
A project-wide search box re-searches the same prose on every keystroke, and folding it is not free. Measured over a 300k-word manuscript: the fold costs about the same as parsing the prose in the first place, and roughly twice what scanning it does — and it was being rebuilt from scratch for every character the writer typed. A host app holds one of these per scene and pays that cost once.
§What it is keyed by, and what it is not
It is built from a FoldSpec — case, diacritics, language — because those are what
decide how a character folds. whole_word is not one of them: it decides which matches
survive, not how the text folds. So it is a parameter of find_all, not
of the fold, and one FoldedText answers both kinds of query. That is not a nicety: it
means ticking “whole word” does not throw away a manuscript’s worth of folding.
The word-boundary table is built lazily, on the first whole-word query, because it is a second full pass over the text and most searches never ask for it.
Implementations§
Source§impl FoldedText
impl FoldedText
Sourcepub fn new(haystack: &str, spec: &FoldSpec) -> FoldedText
pub fn new(haystack: &str, spec: &FoldSpec) -> FoldedText
Fold haystack under spec. (MatchOptions::fold_spec gets you one.)
Sourcepub fn source(&self) -> &str
pub fn source(&self) -> &str
The original text — what the offsets below address, and what a snippet is cut from.
Sourcepub fn find_all(&self, needle: &str, whole_word: bool) -> Vec<Match>
pub fn find_all(&self, needle: &str, whole_word: bool) -> Vec<Match>
Every occurrence of needle, in original char offsets.
whole_word is decided here rather than at fold time; see the type’s docs.
Sourcepub fn prepare_word_boundaries(&self)
pub fn prepare_word_boundaries(&self)
Build the word-boundary table now, rather than on the first whole-word query.
A caller that caches a FoldedText should call this, and the reason is
heap_size, not speed. The table is lazy so that the throwaway
path — fold, search once, drop — does not pay a UAX#29 pass it never uses. But a
cached instance is measured once, when it is stored, and then grows silently the
first time someone ticks “whole word”: its recorded size is stale from that moment on,
and a cache bounded by the sum of those sizes holds materially more than it believes.
Forcing it here makes the size final, so what the cache measures is what it has.
Sourcepub fn heap_size(&self) -> usize
pub fn heap_size(&self) -> usize
Roughly how many bytes of heap this holds.
A host app caching one per scene has to bound the total, and the honest number is not obvious: the index maps are four bytes per folded char, so together they outweigh the text itself several times over.
Not stable until prepare_word_boundaries has been
called — the boundary table is built lazily, so before then this under-reports by
whatever that table will come to weigh.