//! Packet maps. //! //! If configured to do so, a `PacketParser` will create a map that //! charts the byte-stream, describing where the information was //! extracted from. use std::cmp; /// Map created during parsing. #[derive(Clone, Debug)] pub struct Map { length: usize, entries: Vec, header: Vec, data: Vec, } /// Represents an entry in the map. #[derive(Clone, Debug)] struct Entry { offset: usize, length: usize, field: &'static str, } impl Map { /// Creates a new map. pub(crate) fn new(header: Vec) -> Self { Map { length: 0, entries: Vec::new(), header: header, data: Vec::new(), } } /// Adds a field to the map. pub(crate) fn add(&mut self, field: &'static str, length: usize) { self.entries.push(Entry { offset: self.length, length: length, field: field }); self.length += length; } /// Finalizes the map providing the actual data. pub(crate) fn finalize(&mut self, data: Vec) { self.data = data; } /// Creates an iterator over the map. /// /// Items returned are a small string indicating what kind of /// information is extracted (e.g. "header", or "version"), and a /// slice containing the actual bytes. /// /// # Example /// /// ``` /// # extern crate sequoia_openpgp as openpgp; /// # use openpgp::Result; /// # use openpgp::parse::{Parse, PacketParser, PacketParserBuilder}; /// # f(); /// # /// # fn f() -> Result<()> { /// let msg = b"\xcb\x12t\x00\x00\x00\x00\x00Hello world."; /// let ppo = PacketParserBuilder::from_bytes(msg)? /// .map(true).finalize()?; /// assert_eq!(ppo.unwrap().map().unwrap().iter() /// .map(|f| (f.name, f.data)).collect::>(), /// [("frame", &b"\xcb\x12"[..]), /// ("format", b"t"), /// ("filename_len", b"\x00"), /// ("date", b"\x00\x00\x00\x00"), /// ("body", b"Hello world.")]); /// # Ok(()) /// # } /// ``` pub fn iter<'a>(&'a self) -> Iter<'a> { Iter::new(self) } } /// Represents an entry in the map. #[derive(Clone, Debug)] pub struct Field<'a> { /// Name of the field. pub name: &'static str, /// Offset of the field in the packet. pub offset: usize, /// Length of the field. pub length: usize, /// Value of the field. pub data: &'a [u8], } impl<'a> Field<'a> { fn new(map: &'a Map, i: usize) -> Field<'a> { if i == 0 { Field { offset: 0, length: map.header.len(), name: "frame", data: map.header.as_slice() } } else { let len = map.data.len(); let e = &map.entries[i - 1]; let start = cmp::min(len, e.offset); let end = cmp::min(len, e.offset + e.length); Field { offset: map.header.len() + e.offset, length: e.length, name: e.field, data: &map.data[start..end], } } } } /// An iterator over the map. pub struct Iter<'a> { map: &'a Map, i: usize, } impl<'a> Iter<'a> { fn new(map: &'a Map) -> Iter<'a> { Iter { map: map, i: 0, } } } impl<'a> Iterator for Iter<'a> { type Item = Field<'a>; fn next(&mut self) -> Option { if self.i < self.map.entries.len() + 1 { self.i += 1; Some(Field::new(self.map, self.i - 1)) } else { None } } }