#![allow(clippy::len_without_is_empty)] use core::convert::TryInto; use core::ops::Range; use core::{mem, result}; use crate::pod::{from_bytes, slice_from_bytes, Pod}; type Result = result::Result; /// A trait for reading references to `Pod` types from a block of data. /// /// This allows parsers to handle both of these cases: /// - the block of data exists in memory, and it is desirable /// to use references to this block instead of copying it, /// - the block of data exists in storage, and it is desirable /// to read on demand to minimize I/O and memory usage. /// /// The methods accept `self` by value because `Self` is expected to behave /// similar to a reference: it may be a reference with a lifetime of `'a`, /// or it may be a wrapper of a reference. /// /// The `Clone` and `Copy` bounds are for convenience, and since `Self` is /// expected to be similar to a reference, these are easily satisfied. /// /// Object file parsers typically use offsets to locate the structures /// in the block, and will most commonly use the `*_at` methods to /// read a structure at a known offset. /// /// Occasionally file parsers will need to treat the block as a stream, /// and so convenience methods are provided that update an offset with /// the size that was read. // // An alternative would be for methods to accept `&mut self` and use a // `seek` method instead of the `offset` parameters, but this is less // convenient for implementers. pub trait ReadRef<'a>: Clone + Copy { /// The total size of the block of data. fn len(self) -> Result; /// Get a reference to a `u8` slice at the given offset. /// /// Returns an error if offset or size are out of bounds. fn read_bytes_at(self, offset: u64, size: u64) -> Result<&'a [u8]>; /// Get a reference to a delimited `u8` slice which starts at range.start. /// /// Does not include the delimiter. /// /// Returns an error if the range is out of bounds or the delimiter is /// not found in the range. fn read_bytes_at_until(self, range: Range, delimiter: u8) -> Result<&'a [u8]>; /// Get a reference to a `u8` slice at the given offset, and update the offset. /// /// Returns an error if offset or size are out of bounds. fn read_bytes(self, offset: &mut u64, size: u64) -> Result<&'a [u8]> { let bytes = self.read_bytes_at(*offset, size)?; *offset = offset.wrapping_add(size); Ok(bytes) } /// Get a reference to a `Pod` type at the given offset, and update the offset. /// /// Returns an error if offset or size are out of bounds. /// /// The default implementation uses `read_bytes`, and returns an error if /// `read_bytes` does not return bytes with the correct alignment for `T`. /// Implementors may want to provide their own implementation that ensures /// the alignment can be satisified. Alternatively, only use this method with /// types that do not need alignment (see the `unaligned` feature of this crate). fn read(self, offset: &mut u64) -> Result<&'a T> { let size = mem::size_of::().try_into().map_err(|_| ())?; let bytes = self.read_bytes(offset, size)?; let (t, _) = from_bytes(bytes)?; Ok(t) } /// Get a reference to a `Pod` type at the given offset. /// /// Returns an error if offset or size are out of bounds. /// /// Also see the `read` method for information regarding alignment of `T`. fn read_at(self, mut offset: u64) -> Result<&'a T> { self.read(&mut offset) } /// Get a reference to a slice of a `Pod` type at the given offset, and update the offset. /// /// Returns an error if offset or size are out of bounds. /// /// Also see the `read` method for information regarding alignment of `T`. fn read_slice(self, offset: &mut u64, count: usize) -> Result<&'a [T]> { let size = count .checked_mul(mem::size_of::()) .ok_or(())? .try_into() .map_err(|_| ())?; let bytes = self.read_bytes(offset, size)?; let (t, _) = slice_from_bytes(bytes, count)?; Ok(t) } /// Get a reference to a slice of a `Pod` type at the given offset. /// /// Returns an error if offset or size are out of bounds. /// /// Also see the `read` method for information regarding alignment of `T`. fn read_slice_at(self, mut offset: u64, count: usize) -> Result<&'a [T]> { self.read_slice(&mut offset, count) } } impl<'a> ReadRef<'a> for &'a [u8] { fn len(self) -> Result { self.len().try_into().map_err(|_| ()) } fn read_bytes_at(self, offset: u64, size: u64) -> Result<&'a [u8]> { let offset: usize = offset.try_into().map_err(|_| ())?; let size: usize = size.try_into().map_err(|_| ())?; self.get(offset..).ok_or(())?.get(..size).ok_or(()) } fn read_bytes_at_until(self, range: Range, delimiter: u8) -> Result<&'a [u8]> { let start: usize = range.start.try_into().map_err(|_| ())?; let end: usize = range.end.try_into().map_err(|_| ())?; let bytes = self.get(start..end).ok_or(())?; match memchr::memchr(delimiter, bytes) { Some(len) => { // This will never fail. bytes.get(..len).ok_or(()) } None => Err(()), } } }