read_fonts/
array.rs

1//! Custom array types
2
3#![deny(clippy::arithmetic_side_effects)]
4
5use bytemuck::AnyBitPattern;
6use font_types::FixedSize;
7
8use crate::read::{ComputeSize, FontReadWithArgs, ReadArgs, VarSize};
9use crate::{FontData, FontRead, ReadError};
10
11/// An array whose items size is not known at compile time.
12///
13/// This requires the inner type to implement [`FontReadWithArgs`] as well as
14/// [`ComputeSize`].
15///
16/// At runtime, `Args` are provided which will be used to compute the size
17/// of each item; this size is then used to compute the positions of the items
18/// within the underlying data, from which they will be read lazily.
19#[derive(Clone)]
20pub struct ComputedArray<'a, T: ReadArgs> {
21    // the length of each item
22    item_len: usize,
23    len: usize,
24    data: FontData<'a>,
25    args: T::Args,
26}
27
28impl<'a, T: ComputeSize> ComputedArray<'a, T> {
29    pub fn new(data: FontData<'a>, args: T::Args) -> Result<Self, ReadError> {
30        let item_len = T::compute_size(&args)?;
31        let len = data.len().checked_div(item_len).unwrap_or(0);
32        Ok(ComputedArray {
33            item_len,
34            len,
35            data,
36            args,
37        })
38    }
39
40    /// The number of items in the array
41    pub fn len(&self) -> usize {
42        self.len
43    }
44
45    pub fn is_empty(&self) -> bool {
46        self.len == 0
47    }
48}
49
50impl<T: ReadArgs> ReadArgs for ComputedArray<'_, T> {
51    type Args = T::Args;
52}
53
54impl<'a, T> FontReadWithArgs<'a> for ComputedArray<'a, T>
55where
56    T: ComputeSize + FontReadWithArgs<'a>,
57    T::Args: Copy,
58{
59    fn read_with_args(data: FontData<'a>, args: &Self::Args) -> Result<Self, ReadError> {
60        Self::new(data, *args)
61    }
62}
63
64impl<'a, T> ComputedArray<'a, T>
65where
66    T: FontReadWithArgs<'a>,
67    T::Args: Copy + 'static,
68{
69    pub fn iter(&self) -> impl Iterator<Item = Result<T, ReadError>> + 'a {
70        let mut i = 0;
71        let data = self.data;
72        let args = self.args;
73        let item_len = self.item_len;
74        let len = self.len;
75
76        std::iter::from_fn(move || {
77            if i == len {
78                return None;
79            }
80            let item_start = item_len.checked_mul(i)?;
81            i = i.checked_add(1)?;
82            let data = data.split_off(item_start)?;
83            Some(T::read_with_args(data, &args))
84        })
85    }
86
87    pub fn get(&self, idx: usize) -> Result<T, ReadError> {
88        let item_start = idx
89            .checked_mul(self.item_len)
90            .ok_or(ReadError::OutOfBounds)?;
91        self.data
92            .split_off(item_start)
93            .ok_or(ReadError::OutOfBounds)
94            .and_then(|data| T::read_with_args(data, &self.args))
95    }
96}
97
98impl<T: ReadArgs> std::fmt::Debug for ComputedArray<'_, T> {
99    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
100        f.debug_struct("DynSizedArray")
101            .field("bytes", &self.data)
102            .finish()
103    }
104}
105
106/// An array of items of non-uniform length.
107///
108/// Random access into this array cannot be especially efficient, since it requires
109/// a linear scan.
110pub struct VarLenArray<'a, T> {
111    data: FontData<'a>,
112    phantom: std::marker::PhantomData<*const T>,
113}
114
115impl<'a, T: FontRead<'a> + VarSize> VarLenArray<'a, T> {
116    /// Return the item at the provided index.
117    ///
118    /// # Performance
119    ///
120    /// Determining the position of an item in this collection requires looking
121    /// at all the preceding items; that is, it is `O(n)` instead of `O(1)` as
122    /// it would be for a `Vec`.
123    ///
124    /// As a consequence, calling this method in a loop could potentially be
125    /// very slow. If this is something you need to do, it will probably be
126    /// much faster to first collect all the items into a `Vec` beforehand,
127    /// and then fetch them from there.
128    pub fn get(&self, idx: usize) -> Option<Result<T, ReadError>> {
129        let mut pos = 0usize;
130        for _ in 0..idx {
131            pos = pos.checked_add(T::read_len_at(self.data, pos)?)?;
132        }
133        self.data.split_off(pos).map(T::read)
134    }
135
136    /// Return an iterator over this array's items.
137    pub fn iter(&self) -> impl Iterator<Item = Result<T, ReadError>> + 'a {
138        let mut data = self.data;
139        std::iter::from_fn(move || {
140            if data.is_empty() {
141                return None;
142            }
143
144            let item_len = T::read_len_at(data, 0)?;
145            let item_data = data.slice(..item_len)?;
146            let next = T::read(item_data);
147            data = data.split_off(item_len)?;
148            Some(next)
149        })
150    }
151}
152
153impl<'a, T> FontRead<'a> for VarLenArray<'a, T> {
154    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
155        Ok(VarLenArray {
156            data,
157            phantom: core::marker::PhantomData,
158        })
159    }
160}
161
162impl<T: AnyBitPattern> ReadArgs for &[T] {
163    type Args = u16;
164}
165
166impl<'a, T: AnyBitPattern + FixedSize> FontReadWithArgs<'a> for &'a [T] {
167    fn read_with_args(data: FontData<'a>, args: &u16) -> Result<Self, ReadError> {
168        let len = (*args as usize)
169            .checked_mul(T::RAW_BYTE_LEN)
170            .ok_or(ReadError::OutOfBounds)?;
171        data.read_array(0..len)
172    }
173}