1#![deny(clippy::arithmetic_side_effects)]
4
5use bytemuck::AnyBitPattern;
6use font_types::FixedSize;
7
8use crate::read::{ComputeSize, FontRead, FontReadAt, ReadArgs, VarSize};
9use crate::{FontData, ReadError};
10use core::ops::Range;
11
12#[derive(Clone)]
24pub struct ComputedArray<'a, T: ReadArgs> {
25 item_len: usize,
27 len: usize,
28 start: usize,
30 data: FontData<'a>,
31 args: T::Args,
32}
33
34impl<'a, T: ComputeSize> ComputedArray<'a, T> {
35 pub fn new(data: FontData<'a>, range: Range<usize>, args: T::Args) -> Result<Self, ReadError> {
36 let item_len = T::compute_size(args)?;
37 let available = data
40 .as_bytes()
41 .get(range.clone())
42 .ok_or(ReadError::OutOfBounds)?
43 .len();
44 let len = available.checked_div(item_len).unwrap_or(0);
45 Ok(ComputedArray {
46 item_len,
47 len: representable_len(range.start, item_len, len),
48 start: range.start,
49 data,
50 args,
51 })
52 }
53
54 pub fn len(&self) -> usize {
56 self.len
57 }
58
59 pub fn is_empty(&self) -> bool {
60 self.len == 0
61 }
62}
63
64impl<T: ReadArgs> ReadArgs for ComputedArray<'_, T> {
65 type Args = T::Args;
66}
67
68impl<T> Default for ComputedArray<'_, T>
69where
70 T: ReadArgs,
71 T::Args: Default,
72{
73 fn default() -> Self {
74 Self {
75 item_len: 0,
76 len: 0,
77 start: 0,
78 data: Default::default(),
79 args: Default::default(),
80 }
81 }
82}
83
84impl<'a, T> ComputedArray<'a, T>
85where
86 T: FontReadAt<'a>,
87 T::Args: Copy + 'static,
88{
89 pub fn iter(&self) -> impl Iterator<Item = Result<T, ReadError>> + 'a + Clone {
90 let data = self.data;
91 let args = self.args;
92 let item_len = self.item_len;
93 let len = self.len;
94 let mut item_start = self.start;
98
99 (0..len).map(move |_| {
100 let item = T::read_at(data, item_start, args);
101 #[allow(clippy::arithmetic_side_effects)] {
103 item_start += item_len;
104 }
105 item
106 })
107 }
108
109 #[inline]
110 pub fn get(&self, idx: usize) -> Result<T, ReadError> {
111 if idx >= self.len {
112 return Err(ReadError::OutOfBounds);
113 }
114 let item_start = idx
115 .checked_mul(self.item_len)
116 .and_then(|start| start.checked_add(self.start))
117 .ok_or(ReadError::OutOfBounds)?;
118 T::read_at(self.data, item_start, self.args)
119 }
120}
121
122impl<T: ReadArgs> std::fmt::Debug for ComputedArray<'_, T> {
123 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
124 f.debug_struct("DynSizedArray")
125 .field("bytes", &self.data)
126 .finish()
127 }
128}
129
130fn representable_len(start: usize, item_len: usize, len: usize) -> usize {
140 if item_len == 0 {
141 return len;
142 }
143 #[allow(clippy::arithmetic_side_effects)]
145 let max_len = (usize::MAX - start) / item_len;
146 len.min(max_len)
147}
148
149#[macro_export]
157macro_rules! impl_font_read_at {
158 ($typ:ty) => {
159 impl<'a> $crate::FontReadAt<'a> for $typ {
160 fn read_at(
161 data: $crate::FontData<'a>,
162 offset: usize,
163 args: <Self as $crate::ReadArgs>::Args,
164 ) -> Result<Self, $crate::ReadError> {
165 let len = <Self as $crate::ComputeSize>::compute_size(args)?;
166 let end = offset
167 .checked_add(len)
168 .ok_or($crate::ReadError::OutOfBounds)?;
169 let data = data
170 .slice(offset..end)
171 .ok_or($crate::ReadError::OutOfBounds)?;
172 <Self as $crate::FontRead<'a>>::read_with_args(data, args)
173 }
174 }
175 };
176}
177
178pub struct VarLenArray<'a, T> {
183 data: FontData<'a>,
184 phantom: std::marker::PhantomData<*const T>,
185}
186
187impl<'a, T: FontRead<'a, Args = ()> + VarSize> VarLenArray<'a, T> {
188 pub fn get(&self, idx: usize) -> Option<Result<T, ReadError>> {
201 if self.data.is_empty() {
202 return None;
203 }
204 let mut pos = 0usize;
205 for _ in 0..idx {
206 pos = pos.checked_add(T::read_len_at(self.data, pos)?)?;
207 }
208 let len = T::read_len_at(self.data, pos)?;
209 let end = pos.checked_add(len)?;
210 self.data.slice(pos..end).map(T::read)
211 }
212
213 pub fn iter(&self) -> impl Iterator<Item = Result<T, ReadError>> + 'a {
215 let mut data = self.data;
216 std::iter::from_fn(move || {
217 if data.is_empty() {
218 return None;
219 }
220
221 let item_len = T::read_len_at(data, 0)?;
222 if item_len == 0 {
227 return None;
228 }
229 let item_data = data.slice(..item_len)?;
230 let next = T::read(item_data);
231 data = data.split_off(item_len)?;
232 Some(next)
233 })
234 }
235}
236
237impl<T> ReadArgs for VarLenArray<'_, T> {
238 type Args = ();
239}
240
241impl<'a, T> FontRead<'a> for VarLenArray<'a, T> {
242 fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
243 Ok(VarLenArray {
244 data,
245 phantom: core::marker::PhantomData,
246 })
247 }
248}
249
250impl<T> Default for VarLenArray<'_, T> {
251 fn default() -> Self {
252 Self {
253 data: Default::default(),
254 phantom: std::marker::PhantomData,
255 }
256 }
257}
258
259impl<T: AnyBitPattern> ReadArgs for &[T] {
260 type Args = u16;
261}
262
263impl<'a, T: AnyBitPattern + FixedSize> FontRead<'a> for &'a [T] {
264 fn read_with_args(data: FontData<'a>, args: u16) -> Result<Self, ReadError> {
265 let len = (args as usize)
266 .checked_mul(T::RAW_BYTE_LEN)
267 .ok_or(ReadError::OutOfBounds)?;
268 data.read_array(0..len)
269 }
270}
271
272pub(crate) fn get_pair<T>(slice: &[T], idx: usize) -> Result<&[T; 2], ReadError> {
275 slice
276 .get(idx..)
277 .ok_or(ReadError::OutOfBounds)?
278 .first_chunk()
279 .ok_or(ReadError::OutOfBounds)
280}
281
282#[cfg(test)]
283mod tests {
284 use super::*;
285 use crate::codegen_test::records::VarLenItem;
286 use font_test_data::bebuffer::BeBuffer;
287
288 #[test]
291 fn representable_len_bound() {
292 assert_eq!(representable_len(0, 4, 10), 10);
294 assert_eq!(representable_len(100, 4, 10), 10);
295 assert_eq!(representable_len(usize::MAX, 0, 10), 10);
297
298 assert_eq!(representable_len(0, 1, usize::MAX), usize::MAX);
300 assert_eq!(representable_len(2, 1, usize::MAX), usize::MAX - 2);
301 assert_eq!(representable_len(usize::MAX, 1, 5), 0);
302
303 for (start, item_len, len) in [
305 (0usize, 4usize, 10usize),
306 (100, 4, 10),
307 (usize::MAX, 1, 5),
308 (usize::MAX - 8, 4, 100),
309 (usize::MAX / 2, 3, usize::MAX),
310 ] {
311 let bounded = representable_len(start, item_len, len);
312 assert!(bounded <= len);
313 assert!(
314 bounded
315 .checked_mul(item_len)
316 .and_then(|off| off.checked_add(start))
317 .is_some(),
318 "start {start} item_len {item_len} len {len} -> {bounded}"
319 );
320 }
321 }
322
323 impl VarSize for VarLenItem<'_> {
324 type Size = u32;
325
326 fn read_len_at(data: FontData, pos: usize) -> Option<usize> {
327 data.read_at::<u32>(pos).ok().map(|len| len as usize)
328 }
329 }
330
331 #[test]
335 fn var_len_iter_with_zero_length_item() {
336 let mut buf = BeBuffer::new();
339 buf = buf.push(8u32).extend([0u8; 4]);
340 buf = buf.push(18u32).extend([0u8; 14]);
341 buf = buf.push(0u32);
342 let arr: VarLenArray<VarLenItem> = VarLenArray::read(FontData::new(buf.data())).unwrap();
343 assert_eq!(arr.iter().take(10).count(), 2);
347 }
348
349 #[test]
350 fn var_len_iter_same_as_get() {
351 let mut buf = BeBuffer::new();
352 buf = buf.push(4u32).extend([1u8, 2, 3, 4]);
353 buf = buf.push(2u32).extend([5u8, 6]);
354 buf = buf.push(3u32).extend([7u8, 8, 9]);
355 let arr: VarLenArray<VarLenItem> = VarLenArray::read(FontData::new(buf.data())).unwrap();
356 let iter_items: Vec<_> = arr.iter().map(|x| x.unwrap()).collect();
357 let get_items: Vec<_> = (0..iter_items.len())
358 .map(|i| arr.get(i).unwrap().unwrap())
359 .collect();
360 assert_eq!(iter_items.len(), get_items.len());
361 for (a, b) in iter_items.iter().zip(get_items.iter()) {
362 assert_eq!(a.data(), b.data());
363 }
364 }
365}