blackbox_log/
file.rs

1use alloc::vec::Vec;
2use core::fmt;
3
4use memchr::memmem;
5
6use crate::headers::{Headers, ParseResult};
7
8/// A complete blackbox log file containing zero or more logs.
9pub struct File<'data> {
10    offsets: Vec<usize>,
11    data: &'data [u8],
12}
13
14impl<'data> File<'data> {
15    /// Creates a new `File` from a byte slice of its contents.
16    ///
17    /// This is relatively cheap, since it only scans for log start markers
18    /// without parsing any data.
19    pub fn new(data: &'data [u8]) -> Self {
20        let offsets = memmem::find_iter(data, crate::MARKER).collect();
21        Self { offsets, data }
22    }
23
24    /// Returns the number of log start markers in the file.
25    #[inline]
26    pub fn log_count(&self) -> usize {
27        self.offsets.len()
28    }
29
30    /// Returns an iterator over parsed [`Headers`] for each log.
31    ///
32    /// Roughly equivalent to repeatedly calling [`File::parse`], but may
33    /// be able to eliminate bounds checks and skips the `Option`
34    /// wrapper.
35    pub fn iter(&self) -> impl Iterator<Item = ParseResult<Headers<'data>>> + '_ {
36        self.offsets
37            .iter()
38            .map(|&offset| Headers::parse(&self.data[offset..]))
39    }
40
41    /// Attempts to parse the headers of the `index`-th log. Returns `None` if
42    /// there is no log number `index`.
43    pub fn parse(&self, index: usize) -> Option<ParseResult<Headers<'data>>> {
44        let start = *self.offsets.get(index)?;
45        let data = if let Some(&next) = self.offsets.get(index + 1) {
46            &self.data[start..next]
47        } else {
48            &self.data[start..]
49        };
50
51        Some(Headers::parse(data))
52    }
53}
54
55impl fmt::Debug for File<'_> {
56    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57        f.debug_struct("File")
58            .field("offsets", &self.offsets)
59            .finish_non_exhaustive()
60    }
61}