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