//! Arrays of offsets with dynamic resolution //! //! This module provides a number of types that wrap arrays of offsets, dynamically //! resolving individual offsets as they are accessed. use crate::offset::ResolveNullableOffset; use font_types::{BigEndian, Nullable, Offset16, Scalar}; use crate::{FontData, FontReadWithArgs, Offset, ReadArgs, ReadError, ResolveOffset}; /// An array of offsets that can be resolved on access. /// /// This bundles up the raw offsets with the data used to resolve them, along /// with any arguments needed to resolve those offsets; it provides a simple /// ergonomic interface that unburdens the user from needing to manually /// determine the appropriate input data and arguments for a raw offset. #[derive(Clone)] pub struct ArrayOfOffsets<'a, T: ReadArgs, O: Scalar = Offset16> { offsets: &'a [BigEndian], data: FontData<'a>, args: T::Args, } /// An array of nullable offsets that can be resolved on access. /// /// This is identical to [`ArrayOfOffsets`], except that each offset is /// allowed to be null. #[derive(Clone)] pub struct ArrayOfNullableOffsets<'a, T: ReadArgs, O: Scalar = Offset16> { offsets: &'a [BigEndian>], data: FontData<'a>, args: T::Args, } impl<'a, T, O> ArrayOfOffsets<'a, T, O> where O: Scalar, T: ReadArgs, { pub(crate) fn new(offsets: &'a [BigEndian], data: FontData<'a>, args: T::Args) -> Self { Self { offsets, data, args, } } } impl<'a, T, O> ArrayOfOffsets<'a, T, O> where O: Scalar + Offset, T: ReadArgs + FontReadWithArgs<'a>, T::Args: Copy + 'static, { /// The number of offsets in the array pub fn len(&self) -> usize { self.offsets.len() } /// `true` if the array is empty pub fn is_empty(&self) -> bool { self.offsets.is_empty() } /// Resolve the offset at the provided index. /// /// Note: if the index is invalid this will return the `InvalidCollectionIndex` /// error variant instead of `None`. pub fn get(&self, idx: usize) -> Result { self.offsets .get(idx) .ok_or(ReadError::InvalidCollectionIndex(idx as _)) .and_then(|o| o.get().resolve_with_args(self.data, &self.args)) } /// Iterate over all of the offset targets. /// /// Each offset will be resolved as it is encountered. pub fn iter(&self) -> impl Iterator> + 'a { let mut iter = self.offsets.iter(); let args = self.args; let data = self.data; std::iter::from_fn(move || { iter.next() .map(|off| off.get().resolve_with_args(data, &args)) }) } } impl<'a, T, O> ArrayOfNullableOffsets<'a, T, O> where O: Scalar + Offset, T: ReadArgs, { pub(crate) fn new( offsets: &'a [BigEndian>], data: FontData<'a>, args: T::Args, ) -> Self { Self { offsets, data, args, } } } impl<'a, T, O> ArrayOfNullableOffsets<'a, T, O> where O: Scalar + Offset, T: ReadArgs + FontReadWithArgs<'a>, T::Args: Copy + 'static, { /// The number of offsets in the array pub fn len(&self) -> usize { self.offsets.len() } /// `true` if the array is empty pub fn is_empty(&self) -> bool { self.offsets.is_empty() } /// Resolve the offset at the provided index. /// /// This will return `None` only if the offset *exists*, but is null. if the /// provided index does not exist, this will return the `InvalidCollectionIndex` /// error variant. pub fn get(&self, idx: usize) -> Option> { let Some(offset) = self.offsets.get(idx) else { return Some(Err(ReadError::InvalidCollectionIndex(idx as _))); }; offset.get().resolve_with_args(self.data, &self.args) } /// Iterate over all of the offset targets. /// /// Each offset will be resolved as it is encountered. pub fn iter(&self) -> impl Iterator>> + 'a { let mut iter = self.offsets.iter(); let args = self.args; let data = self.data; std::iter::from_fn(move || { iter.next() .map(|off| off.get().resolve_with_args(data, &args)) }) } }