//! Handling offsets use super::read::{FontRead, ReadError}; use crate::{font_data::FontData, read::FontReadWithArgs}; use types::{Nullable, Offset16, Offset24, Offset32}; /// Any offset type. pub trait Offset: Copy { fn to_usize(self) -> usize; fn non_null(self) -> Option { match self.to_usize() { 0 => None, other => Some(other), } } } macro_rules! impl_offset { ($name:ident, $width:literal) => { impl Offset for $name { #[inline] fn to_usize(self) -> usize { self.to_u32() as _ } } }; } impl_offset!(Offset16, 2); impl_offset!(Offset24, 3); impl_offset!(Offset32, 4); /// A helper trait providing a 'resolve' method for offset types pub trait ResolveOffset { fn resolve<'a, T: FontRead<'a>>(&self, data: FontData<'a>) -> Result; fn resolve_with_args<'a, T: FontReadWithArgs<'a>>( &self, data: FontData<'a>, args: &T::Args, ) -> Result; } /// A helper trait providing a 'resolve' method for nullable offset types pub trait ResolveNullableOffset { fn resolve<'a, T: FontRead<'a>>(&self, data: FontData<'a>) -> Option>; fn resolve_with_args<'a, T: FontReadWithArgs<'a>>( &self, data: FontData<'a>, args: &T::Args, ) -> Option>; } impl ResolveNullableOffset for Nullable { fn resolve<'a, T: FontRead<'a>>(&self, data: FontData<'a>) -> Option> { match self.offset().resolve(data) { Ok(thing) => Some(Ok(thing)), Err(ReadError::NullOffset) => None, Err(e) => Some(Err(e)), } } fn resolve_with_args<'a, T: FontReadWithArgs<'a>>( &self, data: FontData<'a>, args: &T::Args, ) -> Option> { match self.offset().resolve_with_args(data, args) { Ok(thing) => Some(Ok(thing)), Err(ReadError::NullOffset) => None, Err(e) => Some(Err(e)), } } } impl ResolveOffset for O { fn resolve<'a, T: FontRead<'a>>(&self, data: FontData<'a>) -> Result { self.non_null() .ok_or(ReadError::NullOffset) .and_then(|off| data.split_off(off).ok_or(ReadError::OutOfBounds)) .and_then(T::read) } fn resolve_with_args<'a, T: FontReadWithArgs<'a>>( &self, data: FontData<'a>, args: &T::Args, ) -> Result { self.non_null() .ok_or(ReadError::NullOffset) .and_then(|off| data.split_off(off).ok_or(ReadError::OutOfBounds)) .and_then(|data| T::read_with_args(data, args)) } }