1//! Arrays of offsets with dynamic resolution
2//!
3//! This module provides a number of types that wrap arrays of offsets, dynamically
4//! resolving individual offsets as they are accessed.
56use crate::offset::ResolveNullableOffset;
7use font_types::{BigEndian, Nullable, Offset16, Scalar};
89use crate::{FontData, FontReadWithArgs, Offset, ReadArgs, ReadError, ResolveOffset};
1011/// An array of offsets that can be resolved on access.
12///
13/// This bundles up the raw offsets with the data used to resolve them, along
14/// with any arguments needed to resolve those offsets; it provides a simple
15/// ergonomic interface that unburdens the user from needing to manually
16/// determine the appropriate input data and arguments for a raw offset.
17#[derive(Clone)]
18pub struct ArrayOfOffsets<'a, T: ReadArgs, O: Scalar = Offset16> {
19 offsets: &'a [BigEndian<O>],
20 data: FontData<'a>,
21 args: T::Args,
22}
2324/// An array of nullable offsets that can be resolved on access.
25///
26/// This is identical to [`ArrayOfOffsets`], except that each offset is
27/// allowed to be null.
28#[derive(Clone)]
29pub struct ArrayOfNullableOffsets<'a, T: ReadArgs, O: Scalar = Offset16> {
30 offsets: &'a [BigEndian<Nullable<O>>],
31 data: FontData<'a>,
32 args: T::Args,
33}
3435impl<'a, T, O> ArrayOfOffsets<'a, T, O>
36where
37O: Scalar,
38 T: ReadArgs,
39{
40pub(crate) fn new(offsets: &'a [BigEndian<O>], data: FontData<'a>, args: T::Args) -> Self {
41Self {
42 offsets,
43 data,
44 args,
45 }
46 }
47}
4849impl<'a, T, O> ArrayOfOffsets<'a, T, O>
50where
51O: Scalar + Offset,
52 T: ReadArgs + FontReadWithArgs<'a>,
53 T::Args: Copy + 'static,
54{
55/// The number of offsets in the array
56pub fn len(&self) -> usize {
57self.offsets.len()
58 }
5960/// `true` if the array is empty
61pub fn is_empty(&self) -> bool {
62self.offsets.is_empty()
63 }
6465/// Resolve the offset at the provided index.
66 ///
67 /// Note: if the index is invalid this will return the `InvalidCollectionIndex`
68 /// error variant instead of `None`.
69pub fn get(&self, idx: usize) -> Result<T, ReadError> {
70self.offsets
71 .get(idx)
72 .ok_or(ReadError::InvalidCollectionIndex(idx as _))
73 .and_then(|o| o.get().resolve_with_args(self.data, &self.args))
74 }
7576/// Iterate over all of the offset targets.
77 ///
78 /// Each offset will be resolved as it is encountered.
79pub fn iter(&self) -> impl Iterator<Item = Result<T, ReadError>> + 'a {
80let mut iter = self.offsets.iter();
81let args = self.args;
82let data = self.data;
83 std::iter::from_fn(move || {
84 iter.next()
85 .map(|off| off.get().resolve_with_args(data, &args))
86 })
87 }
88}
8990impl<'a, T, O> ArrayOfNullableOffsets<'a, T, O>
91where
92O: Scalar + Offset,
93 T: ReadArgs,
94{
95pub(crate) fn new(
96 offsets: &'a [BigEndian<Nullable<O>>],
97 data: FontData<'a>,
98 args: T::Args,
99 ) -> Self {
100Self {
101 offsets,
102 data,
103 args,
104 }
105 }
106}
107108impl<'a, T, O> ArrayOfNullableOffsets<'a, T, O>
109where
110O: Scalar + Offset,
111 T: ReadArgs + FontReadWithArgs<'a>,
112 T::Args: Copy + 'static,
113{
114/// The number of offsets in the array
115pub fn len(&self) -> usize {
116self.offsets.len()
117 }
118119/// `true` if the array is empty
120pub fn is_empty(&self) -> bool {
121self.offsets.is_empty()
122 }
123124/// Resolve the offset at the provided index.
125 ///
126 /// This will return `None` only if the offset *exists*, but is null. if the
127 /// provided index does not exist, this will return the `InvalidCollectionIndex`
128 /// error variant.
129pub fn get(&self, idx: usize) -> Option<Result<T, ReadError>> {
130let Some(offset) = self.offsets.get(idx) else {
131return Some(Err(ReadError::InvalidCollectionIndex(idx as _)));
132 };
133 offset.get().resolve_with_args(self.data, &self.args)
134 }
135136/// Iterate over all of the offset targets.
137 ///
138 /// Each offset will be resolved as it is encountered.
139pub fn iter(&self) -> impl Iterator<Item = Option<Result<T, ReadError>>> + 'a {
140let mut iter = self.offsets.iter();
141let args = self.args;
142let data = self.data;
143 std::iter::from_fn(move || {
144 iter.next()
145 .map(|off| off.get().resolve_with_args(data, &args))
146 })
147 }
148}