Skip to main content

trg/
sniff.rs

1//! Content-based format detection. There is no `-z` flag by design.
2
3/// One tar block: enough to reach the `ustar` magic at offset 257.
4pub const SNIFF_LEN: usize = 512;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub enum Format {
8    Gzip,
9    Zstd,
10    Xz,
11    Tar,
12    Plain,
13}
14
15/// Classify a buffer by its leading bytes. Order matters: the compressed
16/// formats are checked first, because a compressed stream can contain any byte
17/// sequence at all — including a convincing `ustar` at offset 257.
18pub fn sniff(head: &[u8]) -> Format {
19    if head.len() >= 2 && head[0] == 0x1f && head[1] == 0x8b {
20        return Format::Gzip;
21    }
22    if head.len() >= 4 && head[..4] == [0x28, 0xB5, 0x2F, 0xFD] {
23        return Format::Zstd;
24    }
25    if head.len() >= 6 && head[..6] == [0xFD, b'7', b'z', b'X', b'Z', 0x00] {
26        return Format::Xz;
27    }
28    if head.len() >= 262 && &head[257..262] == b"ustar" {
29        return Format::Tar;
30    }
31    Format::Plain
32}
33
34#[cfg(test)]
35mod tests {
36    use super::*;
37
38    #[test]
39    fn detects_gzip_from_magic() {
40        assert_eq!(sniff(&[0x1f, 0x8b, 0x08, 0x00]), Format::Gzip);
41    }
42
43    #[test]
44    fn detects_ustar_tar_at_offset_257() {
45        let mut b = vec![0u8; 512];
46        b[257..262].copy_from_slice(b"ustar");
47        assert_eq!(sniff(&b), Format::Tar);
48    }
49
50    #[test]
51    fn detects_gnu_tar_variant() {
52        let mut b = vec![0u8; 512];
53        b[257..265].copy_from_slice(b"ustar  \0");
54        assert_eq!(sniff(&b), Format::Tar);
55    }
56
57    #[test]
58    fn detects_zstd_from_magic() {
59        assert_eq!(sniff(&[0x28, 0xB5, 0x2F, 0xFD, 0x00]), Format::Zstd);
60    }
61
62    #[test]
63    fn zstd_magic_needs_all_four_bytes() {
64        // A short prefix must not be mistaken for a zstd frame.
65        assert_eq!(sniff(&[0x28, 0xB5, 0x2F]), Format::Plain);
66    }
67
68    #[test]
69    fn zstd_wins_over_a_coincidental_ustar() {
70        let mut b = vec![0u8; 512];
71        b[..4].copy_from_slice(&[0x28, 0xB5, 0x2F, 0xFD]);
72        b[257..262].copy_from_slice(b"ustar");
73        assert_eq!(sniff(&b), Format::Zstd);
74    }
75
76    #[test]
77    fn detects_xz_from_magic() {
78        assert_eq!(sniff(&[0xFD, b'7', b'z', b'X', b'Z', 0x00, 0x00]), Format::Xz);
79    }
80
81    #[test]
82    fn xz_magic_needs_all_six_bytes() {
83        assert_eq!(sniff(&[0xFD, b'7', b'z', b'X', b'Z']), Format::Plain);
84    }
85
86    #[test]
87    fn xz_wins_over_a_coincidental_ustar() {
88        let mut b = vec![0u8; 512];
89        b[..6].copy_from_slice(&[0xFD, b'7', b'z', b'X', b'Z', 0x00]);
90        b[257..262].copy_from_slice(b"ustar");
91        assert_eq!(sniff(&b), Format::Xz);
92    }
93
94    #[test]
95    fn plain_text_is_plain() {
96        assert_eq!(sniff(b"2026-08-24T00:00:00Z GET /index.php\n"), Format::Plain);
97    }
98
99    #[test]
100    fn short_input_is_plain_not_panic() {
101        assert_eq!(sniff(b""), Format::Plain);
102        assert_eq!(sniff(&[0x1f]), Format::Plain);
103    }
104
105    #[test]
106    fn gzip_wins_over_a_coincidental_ustar() {
107        // a gzip stream whose bytes happen to contain "ustar" at 257
108        let mut b = vec![0u8; 512];
109        b[0] = 0x1f; b[1] = 0x8b;
110        b[257..262].copy_from_slice(b"ustar");
111        assert_eq!(sniff(&b), Format::Gzip);
112    }
113}