Skip to content

Commit bf557ce

Browse files
committed
feat!: Revamp & optimize the way paths are stored
This is a very crucial redesign that actually required a lot of consideration to move forward but now it allows us to save a lot of RAM, acutally improves query time and gives an ability to query directories for free
1 parent 5ab8ce9 commit bf557ce

46 files changed

Lines changed: 1369 additions & 3376 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ mimalloc = "0.1.47"
3434
zlob = "1.3.0"
3535

3636
mlua = { version = "0.11.1", features = ["module", "luajit"] }
37-
neo_frizbee = { version = "0.9.1", features = ["match_end_col"] }
37+
neo_frizbee = { version = "0.10.0", features = ["match_end_col"] }
3838
notify = { version = "9.0.0-rc.2" }
3939
notify-debouncer-full = { package="fff-notify-debouncer-full", version = "0.9.0" }
4040
once_cell = "1.20.2"

crates/fff-c/include/fff.h

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@ typedef struct FffResult {
6464
* Free the entire result with `fff_free_search_result`.
6565
*/
6666
typedef struct FffFileItem {
67-
char *path;
6867
char *relative_path;
6968
char *file_name;
7069
char *git_status;
@@ -156,7 +155,6 @@ typedef struct FffMatchRange {
156155
* `FffGrepResult` with `fff_free_grep_result` to release everything.
157156
*/
158157
typedef struct FffGrepMatch {
159-
char *path;
160158
char *relative_path;
161159
char *file_name;
162160
char *git_status;
@@ -380,6 +378,17 @@ struct FffResult *fff_scan_files(void *fff_handle);
380378
*/
381379
bool fff_is_scanning(void *fff_handle);
382380

381+
/**
382+
* Get the base path of the file picker.
383+
*
384+
* Returns an `FffResult` with a heap-allocated C string in the `handle`
385+
* field. Free the string with `fff_free_string` after reading it.
386+
*
387+
* ## Safety
388+
* `fff_handle` must be a valid instance pointer from `fff_create_instance`.
389+
*/
390+
struct FffResult *fff_get_base_path(void *fff_handle);
391+
383392
/**
384393
* Get scan progress information.
385394
*

crates/fff-c/src/ffi_types.rs

Lines changed: 13 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,10 @@
77
use std::ffi::{CString, c_char, c_void};
88
use std::ptr;
99

10+
use fff::file_picker::FilePicker;
1011
use fff::git::format_git_status;
1112
use fff::{FileItem, GrepMatch, GrepResult, Location, Score, SearchResult};
1213

13-
// ---------------------------------------------------------------------------
14-
// Helpers
15-
// ---------------------------------------------------------------------------
16-
1714
/// Allocate a heap CString from a `&str`, returning a raw pointer.
1815
fn cstring_new(s: &str) -> *mut c_char {
1916
CString::new(s).unwrap_or_default().into_raw()
@@ -64,7 +61,6 @@ unsafe fn free_cstring_array(arr: *mut *mut c_char, count: u32) {
6461
/// Free the entire result with `fff_free_search_result`.
6562
#[repr(C)]
6663
pub struct FffFileItem {
67-
pub path: *mut c_char,
6864
pub relative_path: *mut c_char,
6965
pub file_name: *mut c_char,
7066
pub git_status: *mut c_char,
@@ -77,11 +73,11 @@ pub struct FffFileItem {
7773
}
7874

7975
impl FffFileItem {
80-
pub fn from_item(item: &FileItem, arena: *const u8) -> Self {
76+
pub fn from_item(item: &FileItem, picker: &FilePicker) -> Self {
77+
let rel_path = picker.relative_path(item);
8178
FffFileItem {
82-
path: cstring_new(&item.relative_path(arena)),
83-
relative_path: cstring_new(&item.relative_path(arena)),
84-
file_name: cstring_new(&item.file_name(arena)),
79+
relative_path: cstring_new(&rel_path),
80+
file_name: cstring_new(&picker.file_name(item)),
8581
git_status: cstring_new(format_git_status(item.git_status)),
8682
size: item.size,
8783
modified: item.modified,
@@ -98,9 +94,6 @@ impl FffFileItem {
9894
/// All string pointers must have been allocated by `CString::into_raw`.
9995
pub unsafe fn free_strings(&mut self) {
10096
unsafe {
101-
if !self.path.is_null() {
102-
drop(CString::from_raw(self.path));
103-
}
10497
if !self.relative_path.is_null() {
10598
drop(CString::from_raw(self.relative_path));
10699
}
@@ -232,11 +225,11 @@ pub struct FffSearchResult {
232225

233226
impl FffSearchResult {
234227
/// Convert a core `SearchResult` into a heap-allocated `FffSearchResult`.
235-
pub fn from_core(result: &SearchResult, arena: *const u8) -> *mut Self {
228+
pub fn from_core(result: &SearchResult, picker: &FilePicker) -> *mut Self {
236229
let items: Vec<FffFileItem> = result
237230
.items
238231
.iter()
239-
.map(|i| FffFileItem::from_item(i, arena))
232+
.map(|i| FffFileItem::from_item(i, picker))
240233
.collect();
241234
let scores: Vec<FffScore> = result.scores.iter().map(FffScore::from).collect();
242235
let count = items.len() as u32;
@@ -273,7 +266,6 @@ pub struct FffMatchRange {
273266
#[repr(C)]
274267
pub struct FffGrepMatch {
275268
// -- pointers (8 bytes each) --
276-
pub path: *mut c_char,
277269
pub relative_path: *mut c_char,
278270
pub file_name: *mut c_char,
279271
pub git_status: *mut c_char,
@@ -303,7 +295,7 @@ pub struct FffGrepMatch {
303295
}
304296

305297
impl FffGrepMatch {
306-
fn from_core_with_file(m: &GrepMatch, file: &FileItem, arena: *const u8) -> Self {
298+
fn from_core_with_file(m: &GrepMatch, file: &FileItem, picker: &FilePicker) -> Self {
307299
let ranges: Vec<FffMatchRange> = m
308300
.match_byte_offsets
309301
.iter()
@@ -317,10 +309,10 @@ impl FffGrepMatch {
317309
None => (false, 0),
318310
};
319311

312+
let rel_path = picker.relative_path(file);
320313
FffGrepMatch {
321-
path: cstring_new(&file.relative_path(arena)),
322-
relative_path: cstring_new(&file.relative_path(arena)),
323-
file_name: cstring_new(&file.file_name(arena)),
314+
relative_path: cstring_new(&rel_path),
315+
file_name: cstring_new(&picker.file_name(file)),
324316
git_status: cstring_new(format_git_status(file.git_status)),
325317
line_content: cstring_new(&m.line_content),
326318
match_ranges,
@@ -348,9 +340,6 @@ impl FffGrepMatch {
348340
/// All pointers must have been allocated by the corresponding `from_core`.
349341
pub unsafe fn free_fields(&mut self) {
350342
unsafe {
351-
if !self.path.is_null() {
352-
drop(CString::from_raw(self.path));
353-
}
354343
if !self.relative_path.is_null() {
355344
drop(CString::from_raw(self.relative_path));
356345
}
@@ -401,13 +390,13 @@ pub struct FffGrepResult {
401390

402391
impl FffGrepResult {
403392
/// Convert a core `GrepResult` into a heap-allocated `FffGrepResult`.
404-
pub fn from_core(result: &GrepResult, arena: *const u8) -> *mut Self {
393+
pub fn from_core(result: &GrepResult, picker: &FilePicker) -> *mut Self {
405394
let items: Vec<FffGrepMatch> = result
406395
.matches
407396
.iter()
408397
.map(|m| {
409398
let file = result.files[m.file_index];
410-
FffGrepMatch::from_core_with_file(m, file, arena)
399+
FffGrepMatch::from_core_with_file(m, file, picker)
411400
})
412401
.collect();
413402
let (items_ptr, count) = vec_to_raw(items);

crates/fff-c/src/lib.rs

Lines changed: 34 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -294,9 +294,7 @@ pub unsafe extern "C" fn fff_search(
294294
let parser = QueryParser::default();
295295
let parsed = parser.parse(query_str);
296296

297-
let arena = picker.arena_base_ptr();
298-
let results = FilePicker::fuzzy_search(
299-
picker.get_files(),
297+
let results = picker.fuzzy_search(
300298
&parsed,
301299
query_tracker_ref,
302300
FuzzySearchOptions {
@@ -310,10 +308,9 @@ pub unsafe extern "C" fn fff_search(
310308
limit: page_size,
311309
},
312310
},
313-
arena,
314311
);
315312

316-
let search_result = FffSearchResult::from_core(&results, arena);
313+
let search_result = FffSearchResult::from_core(&results, picker);
317314
FffResult::ok_handle(search_result as *mut c_void)
318315
}
319316

@@ -393,11 +390,11 @@ pub unsafe extern "C" fn fff_live_grep(
393390
after_context: after_context as usize,
394391
classify_definitions,
395392
trim_whitespace: false,
393+
abort_signal: None,
396394
};
397395

398-
let arena = picker.arena_base_ptr();
399396
let result = picker.grep(&parsed, &options);
400-
let grep_result = FffGrepResult::from_core(&result, arena);
397+
let grep_result = FffGrepResult::from_core(&result, picker);
401398
FffResult::ok_handle(grep_result as *mut c_void)
402399
}
403400

@@ -496,23 +493,11 @@ pub unsafe extern "C" fn fff_multi_grep(
496493
after_context: after_context as usize,
497494
classify_definitions,
498495
trim_whitespace: false,
496+
abort_signal: None,
499497
};
500498

501-
let arena = picker.arena_base_ptr();
502-
let overlay_guard = picker.bigram_overlay().map(|o| o.read());
503-
let result = fff::multi_grep_search(
504-
picker.get_files(),
505-
&patterns,
506-
constraint_refs,
507-
&options,
508-
picker.cache_budget(),
509-
picker.bigram_index(),
510-
overlay_guard.as_deref(),
511-
None,
512-
picker.base_path(),
513-
arena,
514-
);
515-
let grep_result = FffGrepResult::from_core(&result, arena);
499+
let result = picker.multi_grep(&patterns, constraint_refs, &options);
500+
let grep_result = FffGrepResult::from_core(&result, picker);
516501
FffResult::ok_handle(grep_result as *mut c_void)
517502
}
518503

@@ -561,6 +546,33 @@ pub unsafe extern "C" fn fff_is_scanning(fff_handle: *mut c_void) -> bool {
561546
.unwrap_or(false)
562547
}
563548

549+
/// Get the base path of the file picker.
550+
///
551+
/// Returns an `FffResult` with a heap-allocated C string in the `handle`
552+
/// field. Free the string with `fff_free_string` after reading it.
553+
///
554+
/// ## Safety
555+
/// `fff_handle` must be a valid instance pointer from `fff_create_instance`.
556+
#[unsafe(no_mangle)]
557+
pub unsafe extern "C" fn fff_get_base_path(fff_handle: *mut c_void) -> *mut FffResult {
558+
let inst = match unsafe { instance_ref(fff_handle) } {
559+
Ok(i) => i,
560+
Err(e) => return e,
561+
};
562+
563+
let guard = match inst.picker.read() {
564+
Ok(g) => g,
565+
Err(e) => return FffResult::err(&format!("Failed to acquire file picker lock: {}", e)),
566+
};
567+
568+
let picker = match guard.as_ref() {
569+
Some(p) => p,
570+
None => return FffResult::err("File picker not initialized"),
571+
};
572+
573+
FffResult::ok_string(&picker.base_path().to_string_lossy())
574+
}
575+
564576
/// Get scan progress information.
565577
///
566578
/// ## Safety

crates/fff-core/Cargo.toml

Lines changed: 14 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,18 @@ description = "Faboulous & Fast File Finder - a fast and extremely correct file
1010
path = "src/lib.rs"
1111
crate-type = ["rlib", "staticlib", "cdylib"]
1212

13+
[[bench]]
14+
name = "parse_bench"
15+
harness = false
16+
17+
[[bench]]
18+
name = "bigram_bench"
19+
harness = false
20+
21+
[[bench]]
22+
name = "memmem_bench"
23+
harness = false
24+
1325
[features]
1426
default = []
1527
# Enable C FFI exports
@@ -27,7 +39,7 @@ rayon = { workspace = true }
2739
smallvec = { workspace = true }
2840
thiserror = { workspace = true }
2941
tracing = { workspace = true }
30-
fff-query-parser = { workspace = true , version = "0.5.2" }
42+
fff-query-parser = { workspace = true }
3143

3244
# External dependencies
3345
bindet = { workspace = true }
@@ -38,7 +50,7 @@ libc = "0.2"
3850
git2 = { workspace = true }
3951
glidesort = { workspace = true }
4052
globset = { workspace = true }
41-
fff-grep = { workspace = true , version = "0.5.2" }
53+
fff-grep = { workspace = true }
4254
aho-corasick = "1"
4355
memchr = "2"
4456
heed = { workspace = true }
@@ -69,23 +81,3 @@ dunce = { workspace = true }
6981
criterion = { version = "0.5", features = ["html_reports"] }
7082
rand = { version = "0.8", features = ["small_rng"] }
7183
tempfile = "3.8"
72-
73-
[[bench]]
74-
name = "parse_bench"
75-
harness = false
76-
77-
[[bench]]
78-
name = "bigram_bench"
79-
harness = false
80-
81-
[[bench]]
82-
name = "memmem_bench"
83-
harness = false
84-
85-
[[bin]]
86-
name = "bench_segmented"
87-
path = "src/bin/bench_segmented.rs"
88-
89-
[[bin]]
90-
name = "bench_chunked"
91-
path = "src/bin/bench_chunked.rs"

crates/fff-core/src/background_watcher.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -621,7 +621,7 @@ fn is_git_file(path: &Path) -> bool {
621621
.any(|component| component.as_os_str() == ".git")
622622
}
623623

624-
pub fn is_dotgit_change_affecting_status(changed: &Path, repo: &Option<Repository>) -> bool {
624+
fn is_dotgit_change_affecting_status(changed: &Path, repo: &Option<Repository>) -> bool {
625625
let Some(repo) = repo.as_ref() else {
626626
return false;
627627
};

0 commit comments

Comments
 (0)