Skip to main content

read_fonts/
offset.rs

1//! Handling offsets
2
3use super::read::{FontRead, ReadError};
4use crate::font_data::FontData;
5use types::{Nullable, Offset16, Offset24, Offset32};
6
7/// Any offset type.
8pub trait Offset: Copy {
9    fn to_usize(self) -> usize;
10
11    fn non_null(self) -> Option<usize> {
12        match self.to_usize() {
13            0 => None,
14            other => Some(other),
15        }
16    }
17}
18
19macro_rules! impl_offset {
20    ($name:ident, $width:literal) => {
21        impl Offset for $name {
22            #[inline]
23            fn to_usize(self) -> usize {
24                self.to_u32() as _
25            }
26        }
27    };
28}
29
30impl_offset!(Offset16, 2);
31impl_offset!(Offset24, 3);
32impl_offset!(Offset32, 4);
33
34/// A helper trait providing a 'resolve' method for offset types
35pub trait ResolveOffset {
36    fn resolve_with_args<'a, T: FontRead<'a>>(
37        &self,
38        data: FontData<'a>,
39        args: T::Args,
40    ) -> Result<T, ReadError>;
41
42    /// Resolve an offset to a target requiring no external state (`Args = ()`).
43    fn resolve<'a, T: FontRead<'a, Args = ()>>(&self, data: FontData<'a>) -> Result<T, ReadError> {
44        self.resolve_with_args(data, ())
45    }
46}
47
48/// A helper trait providing a 'resolve' method for nullable offset types
49pub trait ResolveNullableOffset {
50    fn resolve_with_args<'a, T: FontRead<'a>>(
51        &self,
52        data: FontData<'a>,
53        args: T::Args,
54    ) -> Option<Result<T, ReadError>>;
55
56    /// Resolve an offset to a target requiring no external state (`Args = ()`).
57    fn resolve<'a, T: FontRead<'a, Args = ()>>(
58        &self,
59        data: FontData<'a>,
60    ) -> Option<Result<T, ReadError>> {
61        self.resolve_with_args(data, ())
62    }
63}
64
65impl<O: Offset> ResolveNullableOffset for Nullable<O> {
66    fn resolve_with_args<'a, T: FontRead<'a>>(
67        &self,
68        data: FontData<'a>,
69        args: T::Args,
70    ) -> Option<Result<T, ReadError>> {
71        match self.offset().resolve_with_args(data, args) {
72            Ok(thing) => Some(Ok(thing)),
73            Err(ReadError::NullOffset) => None,
74            Err(e) => Some(Err(e)),
75        }
76    }
77}
78
79impl<O: Offset> ResolveOffset for O {
80    fn resolve_with_args<'a, T: FontRead<'a>>(
81        &self,
82        data: FontData<'a>,
83        args: T::Args,
84    ) -> Result<T, ReadError> {
85        self.non_null()
86            .ok_or(ReadError::NullOffset)
87            .and_then(|off| data.split_off(off).ok_or(ReadError::OutOfBounds))
88            .and_then(|data| T::read_with_args(data, args))
89    }
90}
91
92/// Helper for performing checked sequences of arithmetic operations on
93/// offsets.
94#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
95#[repr(transparent)]
96pub(crate) struct CheckedOffset(Option<usize>);
97
98impl CheckedOffset {
99    pub(crate) fn new(offset: usize) -> Self {
100        Self(Some(offset))
101    }
102
103    #[must_use]
104    pub(crate) fn add(self, x: usize) -> Self {
105        Self(self.0.and_then(|o| o.checked_add(x)))
106    }
107
108    #[must_use]
109    pub(crate) fn mul(self, x: usize) -> Self {
110        Self(self.0.and_then(|o| o.checked_mul(x)))
111    }
112
113    pub(crate) fn get(&self) -> Option<usize> {
114        self.0
115    }
116
117    pub(crate) fn ok_or<E>(&self, err: E) -> Result<usize, E> {
118        self.get().ok_or(err)
119    }
120
121    pub(crate) fn ok_or_oob(&self) -> Result<usize, ReadError> {
122        self.ok_or(ReadError::OutOfBounds)
123    }
124}