149 lines
4.3 KiB
Rust
149 lines
4.3 KiB
Rust
//! 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))
|
|
})
|
|
}
|
|
}
|