1pub 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
15pub 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() >= 4 && (0x50..=0x5F).contains(&head[0]) && head[1..4] == [0x2A, 0x4D, 0x18] {
30 return Format::Zstd;
31 }
32 if head.len() >= 6 && head[..6] == [0xFD, b'7', b'z', b'X', b'Z', 0x00] {
33 return Format::Xz;
34 }
35 if head.len() >= 262 && &head[257..262] == b"ustar" {
36 return Format::Tar;
37 }
38 Format::Plain
39}
40
41#[cfg(test)]
42mod tests {
43 use super::*;
44
45 #[test]
46 fn detects_gzip_from_magic() {
47 assert_eq!(sniff(&[0x1f, 0x8b, 0x08, 0x00]), Format::Gzip);
48 }
49
50 #[test]
51 fn detects_ustar_tar_at_offset_257() {
52 let mut b = vec![0u8; 512];
53 b[257..262].copy_from_slice(b"ustar");
54 assert_eq!(sniff(&b), Format::Tar);
55 }
56
57 #[test]
58 fn detects_gnu_tar_variant() {
59 let mut b = vec![0u8; 512];
60 b[257..265].copy_from_slice(b"ustar \0");
61 assert_eq!(sniff(&b), Format::Tar);
62 }
63
64 #[test]
65 fn detects_zstd_from_magic() {
66 assert_eq!(sniff(&[0x28, 0xB5, 0x2F, 0xFD, 0x00]), Format::Zstd);
67 }
68
69 #[test]
74 fn a_skippable_frame_is_zstd() {
75 for low in 0x50..=0x5F {
76 assert_eq!(sniff(&[low, 0x2A, 0x4D, 0x18, 4, 0, 0, 0]), Format::Zstd, "magic {low:#x}");
77 }
78 assert_eq!(sniff(&[0x60, 0x2A, 0x4D, 0x18, 4, 0, 0, 0]), Format::Plain, "just past the range");
79 }
80
81 #[test]
82 fn zstd_magic_needs_all_four_bytes() {
83 assert_eq!(sniff(&[0x28, 0xB5, 0x2F]), Format::Plain);
85 }
86
87 #[test]
88 fn zstd_wins_over_a_coincidental_ustar() {
89 let mut b = vec![0u8; 512];
90 b[..4].copy_from_slice(&[0x28, 0xB5, 0x2F, 0xFD]);
91 b[257..262].copy_from_slice(b"ustar");
92 assert_eq!(sniff(&b), Format::Zstd);
93 }
94
95 #[test]
96 fn detects_xz_from_magic() {
97 assert_eq!(sniff(&[0xFD, b'7', b'z', b'X', b'Z', 0x00, 0x00]), Format::Xz);
98 }
99
100 #[test]
101 fn xz_magic_needs_all_six_bytes() {
102 assert_eq!(sniff(&[0xFD, b'7', b'z', b'X', b'Z']), Format::Plain);
103 }
104
105 #[test]
106 fn xz_wins_over_a_coincidental_ustar() {
107 let mut b = vec![0u8; 512];
108 b[..6].copy_from_slice(&[0xFD, b'7', b'z', b'X', b'Z', 0x00]);
109 b[257..262].copy_from_slice(b"ustar");
110 assert_eq!(sniff(&b), Format::Xz);
111 }
112
113 #[test]
114 fn plain_text_is_plain() {
115 assert_eq!(sniff(b"2026-08-24T00:00:00Z GET /index.php\n"), Format::Plain);
116 }
117
118 #[test]
119 fn short_input_is_plain_not_panic() {
120 assert_eq!(sniff(b""), Format::Plain);
121 assert_eq!(sniff(&[0x1f]), Format::Plain);
122 }
123
124 #[test]
125 fn gzip_wins_over_a_coincidental_ustar() {
126 let mut b = vec![0u8; 512];
128 b[0] = 0x1f; b[1] = 0x8b;
129 b[257..262].copy_from_slice(b"ustar");
130 assert_eq!(sniff(&b), Format::Gzip);
131 }
132}