Skip to content
Open
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
38 changes: 37 additions & 1 deletion crates/fff-core/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -565,14 +565,16 @@ impl FileItem {
return;
}

let Ok(mut file) = std::fs::OpenOptions::new()
let Ok(file) = std::fs::OpenOptions::new()
.write(false)
.read(true)
.open(path)
else {
tracing::error!(path = ?path.display(), "Failed to open indexed file");
return;
};
// only classify the first MAX_FFFILE_SIZE bytes, never read large files to EOF
let mut file = file.take(MAX_FFFILE_SIZE);

loop {
match file.read(chunk) {
Expand All @@ -584,6 +586,7 @@ impl FileItem {
Ok(n) => {
if detect_binary_content(&chunk[..n]) {
self.set_binary(true);
break;
}
}
}
Expand Down Expand Up @@ -1146,3 +1149,36 @@ mod content_cache_budget_tests {
assert_eq!(budget.max_file_size, default.max_file_size);
}
}

#[cfg(test)]
mod detect_binary_tests {
use super::*;

fn classify(content: &[u8]) -> bool {
let root = tempfile::tempdir().unwrap();
let path = root.path().join("f");
std::fs::write(&path, content).unwrap();
let (item, _) = FileItem::new(path.clone(), root.path(), None);
item.detect_binary_per_byte(&path, &mut [0u8; BINARY_CLASSIFICATION_CHUNK_SIZE]);
item.is_binary()
}

#[test]
fn detects_nul_in_small_file() {
let mut content = vec![b'a'; BINARY_CLASSIFICATION_CHUNK_SIZE * 3];
content[10] = 0;
assert!(classify(&content));
assert!(!classify(b"plain text\n"));
}

#[test]
fn reads_only_first_max_size_bytes() {
let mut content = vec![b'a'; MAX_FFFILE_SIZE as usize + 1];
content[0] = 0;
assert!(classify(&content));

content[0] = b'a';
content[MAX_FFFILE_SIZE as usize] = 0;
assert!(!classify(&content));
}
}
Loading