1use std::collections::{HashMap, VecDeque, hash_map::Entry};
2use std::error::Error as StdError;
3use std::fmt;
4use std::hint::cold_path;
5use std::ops::Range;
6use std::str::from_utf8;
7
8use memchr::{memchr, memmem::find};
9
10pub struct Database<C> {
11 cache: C,
12 types: Box<[FrozenType]>,
13 matches: FrozenMatches,
14 children: FrozenChildren,
15 text_idx: u32,
16 binary_idx: u32,
17}
18
19#[derive(Debug, Default)]
20struct Type {
21 matches: Vec<Match>,
22 children: Vec<u32>,
23}
24
25#[derive(Debug)]
26struct Match {
27 cnt: u32,
28 off: u32,
29}
30
31#[derive(Debug)]
32struct FrozenType {
33 mime_type_off: u32,
34 matches: Range<u32>,
35 children: Range<u32>,
36}
37
38type FrozenMatches = Box<[Match]>;
39
40type FrozenChildren = Box<[u32]>;
41
42impl<C> Database<C>
43where
44 C: AsRef<[u8]>,
45{
46 #[cold]
63 pub fn open(cache: C) -> Result<Self, Error> {
64 let (parents, types) = parse_offsets(cache.as_ref()).bail(Error::MalformedOffsets)?;
65
66 let mut parents = parse_parents(cache.as_ref(), parents).bail(Error::MalformedParents)?;
67
68 let mut types = parse_types(cache.as_ref(), types).bail(Error::MalformedTypes)?;
69
70 let (text_off, binary_off) = parents_to_children(cache.as_ref(), &mut parents, &mut types)?;
71
72 let types = sort(types, parents)?;
73
74 Ok(Self::freeze(cache, types, text_off, binary_off))
75 }
76
77 fn freeze(cache: C, types: Vec<(u32, Type)>, text_off: u32, binary_off: u32) -> Self {
78 let mut matches =
79 Vec::with_capacity(types.iter().map(|(_, r#type)| r#type.matches.len()).sum());
80 let mut children =
81 Vec::with_capacity(types.iter().map(|(_, r#type)| r#type.children.len()).sum());
82
83 let types = types
84 .into_iter()
85 .map(|(mime_type_off, r#type)| {
86 let matches_start = u32::try_from(matches.len()).unwrap();
87 let matches_len = u32::try_from(r#type.matches.len()).unwrap();
88 let matches_end = matches_start.checked_add(matches_len).unwrap();
89 matches.extend(r#type.matches);
90
91 let children_start = u32::try_from(children.len()).unwrap();
92 let children_len = u32::try_from(r#type.children.len()).unwrap();
93 let children_end = children_start.checked_add(children_len).unwrap();
94 children.extend(r#type.children);
95
96 FrozenType {
97 mime_type_off,
98 matches: matches_start..matches_end,
99 children: children_start..children_end,
100 }
101 })
102 .collect::<Box<[_]>>();
103
104 let mime_type_off_to_type_idx = types
105 .iter()
106 .enumerate()
107 .map(|(idx, r#type)| (r#type.mime_type_off, u32::try_from(idx).unwrap()))
108 .collect::<HashMap<_, _>>();
109
110 for type_idx in &mut children {
111 *type_idx = mime_type_off_to_type_idx[type_idx];
112 }
113
114 Self {
115 cache,
116 types,
117 matches: matches.into(),
118 children: children.into(),
119 text_idx: mime_type_off_to_type_idx[&text_off],
120 binary_idx: mime_type_off_to_type_idx[&binary_off],
121 }
122 }
123
124 pub fn r#match<'a>(&'a self, bytes: &[u8]) -> Result<&'a str, Error> {
125 let is_text = memchr(0, bytes).is_none();
126
127 let text_type = &self.types[self.text_idx as usize];
128 let binary_type = &self.types[self.binary_idx as usize];
129
130 let mime_type_off = if is_text
131 && let Some(mime_type_off) = self.match_children(None, &text_type.children, bytes)?
132 {
133 mime_type_off
134 } else if let Some(mime_type_off) =
135 self.match_children(None, &binary_type.children, bytes)?
136 {
137 mime_type_off
138 } else if is_text {
139 text_type.mime_type_off
140 } else {
141 binary_type.mime_type_off
142 };
143
144 resolve_mime_type(self.cache.as_ref(), mime_type_off).bail(Error::InvalidMimeType)
145 }
146
147 fn match_children(
148 &self,
149 default: Option<u32>,
150 children: &Range<u32>,
151 bytes: &[u8],
152 ) -> Result<Option<u32>, Error> {
153 let children = &self.children[children.start as usize..children.end as usize];
154
155 for type_idx in children {
156 let r#type = &self.types[*type_idx as usize];
157
158 let matches = &self.matches[r#type.matches.start as usize..r#type.matches.end as usize];
159
160 let mut args = MatchArgs {
161 cache: self.cache.as_ref(),
162 bytes,
163 depth: 0,
164 };
165
166 for r#match in matches {
167 if args
168 .try_match(r#match.cnt, r#match.off)
169 .bail(Error::MalformedMatch)?
170 {
171 return self.match_children(
172 Some(r#type.mime_type_off),
173 &r#type.children,
174 bytes,
175 );
176 }
177 }
178 }
179
180 Ok(default)
181 }
182}
183
184struct MatchArgs<'a> {
185 cache: &'a [u8],
186 bytes: &'a [u8],
187 depth: u8,
188}
189
190impl MatchArgs<'_> {
191 fn try_match(&mut self, cnt: u32, off: u32) -> Option<bool> {
192 let cnt = cnt as usize;
193 let off = off as usize;
194
195 let matchlets = self.cache.get(off..)?.get(..32 * cnt)?;
196
197 for idx in 0..cnt {
198 let matchlet = &matchlets[32 * idx..][..32];
199
200 if self.try_matchlet(matchlet)? {
201 let cnt = parse_u32(matchlet, 24);
202 let off = parse_u32(matchlet, 28);
203
204 if cnt == 0 {
205 return Some(true);
206 }
207
208 self.depth += 1;
209 if self.depth == 128 {
210 return None;
211 }
212
213 if self.try_match(cnt, off)? {
214 return Some(true);
215 }
216
217 self.depth -= 1;
218 }
219 }
220
221 Some(false)
222 }
223
224 #[inline]
225 fn try_matchlet(&self, matchlet: &[u8]) -> Option<bool> {
226 let range_start = parse_u32(matchlet, 0) as usize;
227 let range_length = parse_u32(matchlet, 4) as usize;
228 let data_length = parse_u32(matchlet, 12) as usize;
229 let data_offset = parse_u32(matchlet, 16) as usize;
230 let mask_offset = parse_u32(matchlet, 20) as usize;
231
232 let data = self.cache.get(data_offset..)?.get(..data_length)?;
233
234 let mask = if mask_offset != 0 {
235 Some(self.cache.get(mask_offset..)?.get(..data_length)?)
236 } else {
237 None
238 };
239
240 for pos in range_start..range_start + range_length {
241 if let Some(bytes) = self.bytes.get(pos..pos + data_length) {
242 if let Some(mask) = mask {
243 if data
244 .iter()
245 .zip(mask)
246 .zip(bytes)
247 .all(|((data, mask), byte)| data & mask == byte & mask)
248 {
249 return Some(true);
250 }
251 } else {
252 if compare(data, bytes) {
253 return Some(true);
254 }
255 }
256 } else {
257 break;
258 }
259 }
260
261 Some(false)
262 }
263}
264
265#[inline]
266fn compare(lhs: &[u8], rhs: &[u8]) -> bool {
267 let len = lhs.len();
268 debug_assert_eq!(len, rhs.len());
269
270 if len >= 8 {
271 let lhs_lo = u64::from_le_bytes(lhs[..8].try_into().unwrap());
272 let rhs_lo = u64::from_le_bytes(rhs[..8].try_into().unwrap());
273 if lhs_lo != rhs_lo {
274 return false;
275 } else if len == 8 {
276 return true;
277 }
278
279 let lhs_hi = u64::from_le_bytes(lhs[len - 8..].try_into().unwrap());
280 let rhs_hi = u64::from_le_bytes(rhs[len - 8..].try_into().unwrap());
281 if lhs_hi != rhs_hi {
282 return false;
283 } else if len <= 16 {
284 return true;
285 }
286 } else if len >= 4 {
287 let lhs_lo = u32::from_le_bytes(lhs[..4].try_into().unwrap());
288 let rhs_lo = u32::from_le_bytes(rhs[..4].try_into().unwrap());
289 if lhs_lo != rhs_lo {
290 return false;
291 }
292
293 let lhs_hi = u32::from_le_bytes(lhs[len - 4..].try_into().unwrap());
294 let rhs_hi = u32::from_le_bytes(rhs[len - 4..].try_into().unwrap());
295 return lhs_hi == rhs_hi;
296 } else if len > 0 {
297 let lhs_lo = lhs[0];
298 let rhs_lo = rhs[0];
299 if lhs_lo != rhs_lo {
300 return false;
301 }
302
303 let lhs_mid = lhs[len / 2];
304 let rhs_mid = rhs[len / 2];
305 if lhs_mid != rhs_mid {
306 return false;
307 }
308
309 let lhs_hi = lhs[len - 1];
310 let rhs_hi = rhs[len - 1];
311 return lhs_hi == rhs_hi;
312 } else {
313 return true;
314 }
315
316 lhs == rhs
317}
318
319fn sort(
320 mut types: HashMap<u32, Type>,
321 mut parents: HashMap<u32, Vec<u32>>,
322) -> Result<Vec<(u32, Type)>, Error> {
323 let mut sorted = Vec::with_capacity(types.len());
324
325 let mut orphans = types
326 .keys()
327 .copied()
328 .filter(|mime_type_off| !parents.contains_key(mime_type_off))
329 .collect::<VecDeque<_>>();
330
331 while let Some(orphan_mime_type_off) = orphans.pop_back() {
332 let orphan_type = types.remove(&orphan_mime_type_off).unwrap();
333
334 for child_mime_type_off in &orphan_type.children {
335 match parents.entry(*child_mime_type_off) {
336 Entry::Occupied(mut entry) => {
337 let parents = entry.get_mut();
338
339 parents.retain(|parent_mime_type_off| {
340 *parent_mime_type_off != orphan_mime_type_off
341 });
342
343 if parents.is_empty() {
344 entry.remove();
345
346 orphans.push_front(*child_mime_type_off);
347 }
348 }
349 Entry::Vacant(_) => unreachable!(),
350 }
351 }
352
353 sorted.push((orphan_mime_type_off, orphan_type));
354 }
355
356 if parents.is_empty() {
357 Ok(sorted)
358 } else {
359 Err(Error::MalformedParents)
360 }
361}
362
363fn parents_to_children(
364 cache: &[u8],
365 parents: &mut HashMap<u32, Vec<u32>>,
366 types: &mut HashMap<u32, Type>,
367) -> Result<(u32, u32), Error> {
368 types.retain(|mime_type_off, _type| resolve_mime_type(cache, *mime_type_off).is_some());
369
370 parents.retain(|mime_type_off, parents| {
371 if types.contains_key(mime_type_off) {
372 parents.retain(|parent_mime_type_off| types.contains_key(parent_mime_type_off));
373
374 !parents.is_empty()
375 } else {
376 false
377 }
378 });
379
380 let text_off =
381 find_or_insert_mime_type(cache, types, "text/plain\0").bail(Error::MissingTextPlain)?;
382
383 let binary_off = find_or_insert_mime_type(cache, types, "application/octet-stream\0")
384 .bail(Error::MissingApplicationOctetStream)?;
385
386 for mime_type_off in types.keys() {
387 if *mime_type_off != text_off
388 && *mime_type_off != binary_off
389 && let Entry::Vacant(entry) = parents.entry(*mime_type_off)
390 {
391 let mime_type = resolve_mime_type(cache, *mime_type_off).unwrap();
392
393 let parent_mime_type_off = if mime_type.starts_with("text/") {
394 text_off
395 } else {
396 binary_off
397 };
398
399 entry.insert(vec![parent_mime_type_off]);
400 }
401 }
402
403 for (mime_type_off, parents) in parents {
404 for parent_mime_type_off in parents {
405 types
406 .get_mut(parent_mime_type_off)
407 .unwrap()
408 .children
409 .push(*mime_type_off);
410 }
411 }
412
413 for r#type in types.values_mut() {
414 r#type.children.sort_unstable();
415 r#type.children.dedup();
416 }
417
418 Ok((text_off, binary_off))
419}
420
421fn find_or_insert_mime_type(
422 cache: &[u8],
423 types: &mut HashMap<u32, Type>,
424 mime_type0: &str,
425) -> Option<u32> {
426 let mime_type = &mime_type0[..mime_type0.len() - 1];
427
428 let mime_type_off = types
429 .keys()
430 .copied()
431 .find(|mime_type_off| resolve_mime_type(cache, *mime_type_off) == Some(mime_type));
432
433 mime_type_off.or_else(|| {
434 let mime_type_off = find(cache, mime_type0.as_bytes())?.try_into().ok()?;
435
436 types.insert(mime_type_off, Default::default());
437
438 Some(mime_type_off)
439 })
440}
441
442fn resolve_mime_type(cache: &[u8], mime_type_off: u32) -> Option<&str> {
443 let off = mime_type_off as usize;
444 let bytes = cache.get(off..)?;
445
446 let pos = memchr(0, bytes)?;
447 let mime_type = &bytes[..pos];
448
449 from_utf8(mime_type).ok()
450}
451
452fn parse_offsets(cache: &[u8]) -> Option<(&[u8], &[u8])> {
453 if cache.len() < 2 * 2 + 9 * 4 {
454 return None;
455 }
456
457 let parents_off = parse_u32(cache, 2 * 2 + 4) as usize;
458 let types_off = parse_u32(cache, 2 * 2 + 5 * 4) as usize;
459
460 let parents = cache.get(parents_off..)?;
461 let types = cache.get(types_off..)?;
462
463 Some((parents, types))
464}
465
466fn parse_parents<'a>(cache: &'a [u8], bytes: &'a [u8]) -> Option<HashMap<u32, Vec<u32>>> {
467 if bytes.len() < 4 {
468 return None;
469 }
470
471 let cnt = parse_u32(bytes, 0) as usize;
472
473 let bytes = bytes.get(4..)?.get(..8 * cnt)?;
474
475 let mut parents = HashMap::<_, Vec<_>>::with_capacity(cnt);
476
477 for idx in 0..cnt {
478 let bytes = &bytes[8 * idx..][..8];
479
480 let mime_type_off = parse_u32(bytes, 0);
481 let parents_off = parse_u32(bytes, 4) as usize;
482
483 let bytes = cache.get(parents_off..)?;
484
485 if bytes.len() < 4 {
486 return None;
487 }
488
489 let cnt = parse_u32(bytes, 0) as usize;
490
491 let bytes = bytes.get(4..)?.get(..4 * cnt)?;
492
493 let parents = parents.entry(mime_type_off).or_default();
494 parents.reserve(cnt);
495
496 for idx in 0..cnt {
497 let parent_mime_type_off = parse_u32(bytes, 4 * idx);
498
499 parents.push(parent_mime_type_off);
500 }
501 }
502
503 Some(parents)
504}
505
506fn parse_types(cache: &[u8], bytes: &[u8]) -> Option<HashMap<u32, Type>> {
507 if bytes.len() < 3 * 4 {
508 return None;
509 }
510
511 let cnt = parse_u32(bytes, 0) as usize;
512 let off = parse_u32(bytes, 8) as usize;
513
514 let bytes = cache.get(off..)?.get(..16 * cnt)?;
515
516 let mut types = HashMap::<_, Type>::with_capacity(cnt);
517
518 for idx in 0..cnt {
519 let bytes = &bytes[16 * idx..];
520
521 let mime_type_off = parse_u32(bytes, 4);
522
523 let cnt = parse_u32(bytes, 8);
524 let off = parse_u32(bytes, 12);
525
526 types
527 .entry(mime_type_off)
528 .or_default()
529 .matches
530 .push(Match { cnt, off });
531 }
532
533 Some(types)
534}
535
536fn parse_u32(bytes: &[u8], off: usize) -> u32 {
537 u32::from_be_bytes(bytes[off..][..4].try_into().unwrap())
538}
539
540#[derive(Debug, Clone, Copy)]
541pub enum Error {
542 MalformedOffsets,
543 MalformedParents,
544 MalformedTypes,
545 MalformedMatch,
546 MissingTextPlain,
547 MissingApplicationOctetStream,
548 InvalidMimeType,
549}
550
551trait BailExt<T> {
552 fn bail(self, err: Error) -> Result<T, Error>;
553}
554
555impl<T> BailExt<T> for Option<T> {
556 #[inline(always)]
557 fn bail(self, err: Error) -> Result<T, Error> {
558 self.ok_or_else(|| {
559 cold_path();
560 err
561 })
562 }
563}
564
565impl fmt::Display for Error {
566 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
567 let msg = match self {
568 Self::MalformedOffsets => "Malformed offsets",
569 Self::MalformedParents => "Malformed parents",
570 Self::MalformedTypes => "Malformed types",
571 Self::MalformedMatch => "Malformed match",
572 Self::MissingTextPlain => "Missing text/plain MIME type",
573 Self::MissingApplicationOctetStream => "Missing application/octet-stream MIME type",
574 Self::InvalidMimeType => "Invalid MIME type",
575 };
576
577 fmt.write_str(msg)
578 }
579}
580
581impl StdError for Error {}