1pub fn is_binary(bytes: &[u8]) -> bool {
7 const WINDOW: usize = 8 * 1024;
8 let sample = &bytes[..bytes.len().min(WINDOW)];
9 if sample.is_empty() {
10 return false;
11 }
12 if sample.contains(&0) {
13 return true;
14 }
15 let control = sample
16 .iter()
17 .filter(|&&b| (b < 0x20 && b != b'\n' && b != b'\t' && b != b'\r') || b == 0x7f)
18 .count();
19 control * 10 > sample.len()
20}
21
22pub fn sanitize(bytes: &[u8]) -> String {
26 let text = String::from_utf8_lossy(bytes);
27 text.chars()
28 .filter(|&c| c == '\n' || c == '\t' || !is_control(c))
29 .collect()
30}
31
32pub fn sanitize_inline(name: &str) -> String {
34 name.chars().filter(|&c| !is_control(c)).collect()
35}
36
37fn is_control(c: char) -> bool {
38 (c as u32) < 0x20 || (0x7f..=0x9f).contains(&(c as u32))
39}
40
41#[cfg(test)]
42mod tests {
43 use super::*;
44
45 #[test]
46 fn strips_escape_and_control_bytes() {
47 let raw = b"hello \x1b[2Jevil\x07 world\nkeep\ttabs";
48 assert_eq!(sanitize(raw), "hello [2Jevil world\nkeep\ttabs");
49 }
50
51 #[test]
52 fn invalid_utf8_becomes_replacement_char() {
53 let raw = b"ok \xff\xfe bytes";
54 let out = sanitize(raw);
55 assert!(out.contains('\u{FFFD}'));
56 assert!(out.starts_with("ok "));
57 }
58
59 #[test]
60 fn detects_binary_by_nul() {
61 assert!(is_binary(b"\x7fELF\x00\x01\x02"));
62 assert!(!is_binary(b"fn main() {}\n"));
63 }
64
65 #[test]
66 fn detects_binary_by_control_ratio() {
67 let mut raw = vec![0x01u8; 256];
68 raw.extend_from_slice(b"some text");
69 assert!(is_binary(&raw));
70 }
71
72 #[test]
73 fn inline_drops_newlines_and_tabs() {
74 assert_eq!(sanitize_inline("a\nb\tc\x1bd"), "abcd");
75 }
76}