// THIS FILE IS AUTOGENERATED.
// Any changes to this file will be overwritten.
// For more information about how codegen works, see font-codegen/README.md

#[allow(unused_imports)]
use crate::codegen_prelude::*;

impl<'a> MinByteRange<'a> for Gsub<'a> {
    fn min_byte_range(&self) -> Range<usize> {
        0..self.lookup_list_offset_byte_range().end
    }
    fn min_table_bytes(&self) -> &'a [u8] {
        let range = self.min_byte_range();
        self.data.as_bytes().get(range).unwrap_or_default()
    }
}

impl TopLevelTable for Gsub<'_> {
    /// `GSUB`
    const TAG: Tag = Tag::new(b"GSUB");
}

impl<'a> FontRead<'a> for Gsub<'a> {
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
        #[allow(clippy::absurd_extreme_comparisons)]
        if data.len() < Self::MIN_SIZE {
            return Err(ReadError::OutOfBounds);
        }
        Ok(Self { data })
    }
}

/// [GSUB](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#gsub-header)
#[derive(Clone)]
pub struct Gsub<'a> {
    data: FontData<'a>,
}

#[allow(clippy::needless_lifetimes)]
impl<'a> Gsub<'a> {
    pub const MIN_SIZE: usize = (MajorMinor::RAW_BYTE_LEN
        + Offset16::RAW_BYTE_LEN
        + Offset16::RAW_BYTE_LEN
        + Offset16::RAW_BYTE_LEN);
    basic_table_impls!(impl_the_methods);

    /// The major and minor version of the GSUB table, as a tuple (u16, u16)
    pub fn version(&self) -> MajorMinor {
        let range = self.version_byte_range();
        self.data.read_at(range.start).ok().unwrap()
    }

    /// Offset to ScriptList table, from beginning of GSUB table
    pub fn script_list_offset(&self) -> Offset16 {
        let range = self.script_list_offset_byte_range();
        self.data.read_at(range.start).ok().unwrap()
    }

    /// Attempt to resolve [`script_list_offset`][Self::script_list_offset].
    pub fn script_list(&self) -> Result<ScriptList<'a>, ReadError> {
        let data = self.data;
        self.script_list_offset().resolve(data)
    }

    /// Offset to FeatureList table, from beginning of GSUB table
    pub fn feature_list_offset(&self) -> Offset16 {
        let range = self.feature_list_offset_byte_range();
        self.data.read_at(range.start).ok().unwrap()
    }

    /// Attempt to resolve [`feature_list_offset`][Self::feature_list_offset].
    pub fn feature_list(&self) -> Result<FeatureList<'a>, ReadError> {
        let data = self.data;
        self.feature_list_offset().resolve(data)
    }

    /// Offset to LookupList table, from beginning of GSUB table
    pub fn lookup_list_offset(&self) -> Offset16 {
        let range = self.lookup_list_offset_byte_range();
        self.data.read_at(range.start).ok().unwrap()
    }

    /// Attempt to resolve [`lookup_list_offset`][Self::lookup_list_offset].
    pub fn lookup_list(&self) -> Result<SubstitutionLookupList<'a>, ReadError> {
        let data = self.data;
        self.lookup_list_offset().resolve(data)
    }

    /// Offset to FeatureVariations table, from beginning of the GSUB
    /// table (may be NULL)
    pub fn feature_variations_offset(&self) -> Option<Nullable<Offset32>> {
        let range = self.feature_variations_offset_byte_range();
        (!range.is_empty())
            .then(|| self.data.read_at(range.start).ok())
            .flatten()
    }

    /// Attempt to resolve [`feature_variations_offset`][Self::feature_variations_offset].
    pub fn feature_variations(&self) -> Option<Result<FeatureVariations<'a>, ReadError>> {
        let data = self.data;
        self.feature_variations_offset().map(|x| x.resolve(data))?
    }

    pub fn version_byte_range(&self) -> Range<usize> {
        let start = 0;
        start..start + MajorMinor::RAW_BYTE_LEN
    }

    pub fn script_list_offset_byte_range(&self) -> Range<usize> {
        let start = self.version_byte_range().end;
        start..start + Offset16::RAW_BYTE_LEN
    }

    pub fn feature_list_offset_byte_range(&self) -> Range<usize> {
        let start = self.script_list_offset_byte_range().end;
        start..start + Offset16::RAW_BYTE_LEN
    }

    pub fn lookup_list_offset_byte_range(&self) -> Range<usize> {
        let start = self.feature_list_offset_byte_range().end;
        start..start + Offset16::RAW_BYTE_LEN
    }

    pub fn feature_variations_offset_byte_range(&self) -> Range<usize> {
        let start = self.lookup_list_offset_byte_range().end;
        start
            ..(self.version().compatible((1u16, 1u16)))
                .then(|| start + Offset32::RAW_BYTE_LEN)
                .unwrap_or(start)
    }
}

const _: () = assert!(FontData::default_data_long_enough(Gsub::MIN_SIZE));

impl Default for Gsub<'_> {
    fn default() -> Self {
        Self {
            data: FontData::default_table_data(),
        }
    }
}

#[cfg(feature = "experimental_traverse")]
impl<'a> SomeTable<'a> for Gsub<'a> {
    fn type_name(&self) -> &str {
        "Gsub"
    }
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
        match idx {
            0usize => Some(Field::new("version", self.version())),
            1usize => Some(Field::new(
                "script_list_offset",
                FieldType::offset(self.script_list_offset(), self.script_list()),
            )),
            2usize => Some(Field::new(
                "feature_list_offset",
                FieldType::offset(self.feature_list_offset(), self.feature_list()),
            )),
            3usize => Some(Field::new(
                "lookup_list_offset",
                FieldType::offset(self.lookup_list_offset(), self.lookup_list()),
            )),
            4usize if self.version().compatible((1u16, 1u16)) => Some(Field::new(
                "feature_variations_offset",
                FieldType::offset(
                    self.feature_variations_offset().unwrap(),
                    self.feature_variations(),
                ),
            )),
            _ => None,
        }
    }
}

#[cfg(feature = "experimental_traverse")]
#[allow(clippy::needless_lifetimes)]
impl<'a> std::fmt::Debug for Gsub<'a> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        (self as &dyn SomeTable<'a>).fmt(f)
    }
}

/// A [GSUB Lookup](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#gsubLookupTypeEnum) subtable.
pub enum SubstitutionLookup<'a> {
    Single(Lookup<'a, SingleSubst<'a>>),
    Multiple(Lookup<'a, MultipleSubstFormat1<'a>>),
    Alternate(Lookup<'a, AlternateSubstFormat1<'a>>),
    Ligature(Lookup<'a, LigatureSubstFormat1<'a>>),
    Contextual(Lookup<'a, SubstitutionSequenceContext<'a>>),
    ChainContextual(Lookup<'a, SubstitutionChainContext<'a>>),
    Extension(Lookup<'a, ExtensionSubtable<'a>>),
    Reverse(Lookup<'a, ReverseChainSingleSubstFormat1<'a>>),
}

impl Default for SubstitutionLookup<'_> {
    fn default() -> Self {
        Self::Single(Default::default())
    }
}

impl<'a> FontRead<'a> for SubstitutionLookup<'a> {
    fn read(bytes: FontData<'a>) -> Result<Self, ReadError> {
        let discriminant = Lookup::read_discriminant(bytes)?;
        match discriminant {
            1 => Ok(SubstitutionLookup::Single(FontRead::read(bytes)?)),
            2 => Ok(SubstitutionLookup::Multiple(FontRead::read(bytes)?)),
            3 => Ok(SubstitutionLookup::Alternate(FontRead::read(bytes)?)),
            4 => Ok(SubstitutionLookup::Ligature(FontRead::read(bytes)?)),
            5 => Ok(SubstitutionLookup::Contextual(FontRead::read(bytes)?)),
            6 => Ok(SubstitutionLookup::ChainContextual(FontRead::read(bytes)?)),
            7 => Ok(SubstitutionLookup::Extension(FontRead::read(bytes)?)),
            8 => Ok(SubstitutionLookup::Reverse(FontRead::read(bytes)?)),
            other => Err(ReadError::InvalidFormat(other.into())),
        }
    }
}

impl<'a> SubstitutionLookup<'a> {
    #[allow(dead_code)]
    /// Return the inner table, removing the specific generics.
    ///
    /// This lets us return a single concrete type we can call methods on.
    pub(crate) fn of_unit_type(&self) -> Lookup<'a, ()> {
        match self {
            SubstitutionLookup::Single(inner) => inner.of_unit_type(),
            SubstitutionLookup::Multiple(inner) => inner.of_unit_type(),
            SubstitutionLookup::Alternate(inner) => inner.of_unit_type(),
            SubstitutionLookup::Ligature(inner) => inner.of_unit_type(),
            SubstitutionLookup::Contextual(inner) => inner.of_unit_type(),
            SubstitutionLookup::ChainContextual(inner) => inner.of_unit_type(),
            SubstitutionLookup::Extension(inner) => inner.of_unit_type(),
            SubstitutionLookup::Reverse(inner) => inner.of_unit_type(),
        }
    }
}

#[cfg(feature = "experimental_traverse")]
impl<'a> SubstitutionLookup<'a> {
    fn dyn_inner(&self) -> &(dyn SomeTable<'a> + 'a) {
        match self {
            SubstitutionLookup::Single(table) => table,
            SubstitutionLookup::Multiple(table) => table,
            SubstitutionLookup::Alternate(table) => table,
            SubstitutionLookup::Ligature(table) => table,
            SubstitutionLookup::Contextual(table) => table,
            SubstitutionLookup::ChainContextual(table) => table,
            SubstitutionLookup::Extension(table) => table,
            SubstitutionLookup::Reverse(table) => table,
        }
    }
}

#[cfg(feature = "experimental_traverse")]
impl<'a> SomeTable<'a> for SubstitutionLookup<'a> {
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
        self.dyn_inner().get_field(idx)
    }
    fn type_name(&self) -> &str {
        self.dyn_inner().type_name()
    }
}

#[cfg(feature = "experimental_traverse")]
impl std::fmt::Debug for SubstitutionLookup<'_> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.dyn_inner().fmt(f)
    }
}

/// LookupType 1: [Single Substitution](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#lookuptype-1-single-substitution-subtable) Subtable
#[derive(Clone)]
pub enum SingleSubst<'a> {
    Format1(SingleSubstFormat1<'a>),
    Format2(SingleSubstFormat2<'a>),
}

impl Default for SingleSubst<'_> {
    fn default() -> Self {
        Self::Format1(Default::default())
    }
}

impl<'a> SingleSubst<'a> {
    ///Return the `FontData` used to resolve offsets for this table.
    pub fn offset_data(&self) -> FontData<'a> {
        match self {
            Self::Format1(item) => item.offset_data(),
            Self::Format2(item) => item.offset_data(),
        }
    }

    /// Format identifier: format = 1
    pub fn subst_format(&self) -> u16 {
        match self {
            Self::Format1(item) => item.subst_format(),
            Self::Format2(item) => item.subst_format(),
        }
    }

    /// Offset to Coverage table, from beginning of substitution
    /// subtable
    pub fn coverage_offset(&self) -> Offset16 {
        match self {
            Self::Format1(item) => item.coverage_offset(),
            Self::Format2(item) => item.coverage_offset(),
        }
    }
}

impl<'a> FontRead<'a> for SingleSubst<'a> {
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
        let format: u16 = data.read_at(0usize)?;
        match format {
            SingleSubstFormat1::FORMAT => Ok(Self::Format1(FontRead::read(data)?)),
            SingleSubstFormat2::FORMAT => Ok(Self::Format2(FontRead::read(data)?)),
            other => Err(ReadError::InvalidFormat(other.into())),
        }
    }
}

impl<'a> MinByteRange<'a> for SingleSubst<'a> {
    fn min_byte_range(&self) -> Range<usize> {
        match self {
            Self::Format1(item) => item.min_byte_range(),
            Self::Format2(item) => item.min_byte_range(),
        }
    }
    fn min_table_bytes(&self) -> &'a [u8] {
        match self {
            Self::Format1(item) => item.min_table_bytes(),
            Self::Format2(item) => item.min_table_bytes(),
        }
    }
}

#[cfg(feature = "experimental_traverse")]
impl<'a> SingleSubst<'a> {
    fn dyn_inner<'b>(&'b self) -> &'b dyn SomeTable<'a> {
        match self {
            Self::Format1(table) => table,
            Self::Format2(table) => table,
        }
    }
}

#[cfg(feature = "experimental_traverse")]
impl std::fmt::Debug for SingleSubst<'_> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.dyn_inner().fmt(f)
    }
}

#[cfg(feature = "experimental_traverse")]
impl<'a> SomeTable<'a> for SingleSubst<'a> {
    fn type_name(&self) -> &str {
        self.dyn_inner().type_name()
    }
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
        self.dyn_inner().get_field(idx)
    }
}

impl Format<u16> for SingleSubstFormat1<'_> {
    const FORMAT: u16 = 1;
}

impl<'a> MinByteRange<'a> for SingleSubstFormat1<'a> {
    fn min_byte_range(&self) -> Range<usize> {
        0..self.delta_glyph_id_byte_range().end
    }
    fn min_table_bytes(&self) -> &'a [u8] {
        let range = self.min_byte_range();
        self.data.as_bytes().get(range).unwrap_or_default()
    }
}

impl<'a> FontRead<'a> for SingleSubstFormat1<'a> {
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
        #[allow(clippy::absurd_extreme_comparisons)]
        if data.len() < Self::MIN_SIZE {
            return Err(ReadError::OutOfBounds);
        }
        Ok(Self { data })
    }
}

/// [Single Substitution Format 1](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#11-single-substitution-format-1)
#[derive(Clone)]
pub struct SingleSubstFormat1<'a> {
    data: FontData<'a>,
}

#[allow(clippy::needless_lifetimes)]
impl<'a> SingleSubstFormat1<'a> {
    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN + i16::RAW_BYTE_LEN);
    basic_table_impls!(impl_the_methods);

    /// Format identifier: format = 1
    pub fn subst_format(&self) -> u16 {
        let range = self.subst_format_byte_range();
        self.data.read_at(range.start).ok().unwrap()
    }

    /// Offset to Coverage table, from beginning of substitution
    /// subtable
    pub fn coverage_offset(&self) -> Offset16 {
        let range = self.coverage_offset_byte_range();
        self.data.read_at(range.start).ok().unwrap()
    }

    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
        let data = self.data;
        self.coverage_offset().resolve(data)
    }

    /// Add to original glyph ID to get substitute glyph ID
    pub fn delta_glyph_id(&self) -> i16 {
        let range = self.delta_glyph_id_byte_range();
        self.data.read_at(range.start).ok().unwrap()
    }

    pub fn subst_format_byte_range(&self) -> Range<usize> {
        let start = 0;
        start..start + u16::RAW_BYTE_LEN
    }

    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
        let start = self.subst_format_byte_range().end;
        start..start + Offset16::RAW_BYTE_LEN
    }

    pub fn delta_glyph_id_byte_range(&self) -> Range<usize> {
        let start = self.coverage_offset_byte_range().end;
        start..start + i16::RAW_BYTE_LEN
    }
}

const _: () = assert!(FontData::default_data_long_enough(
    SingleSubstFormat1::MIN_SIZE
));

impl Default for SingleSubstFormat1<'_> {
    fn default() -> Self {
        Self {
            data: FontData::default_format_1_u16_table_data(),
        }
    }
}

#[cfg(feature = "experimental_traverse")]
impl<'a> SomeTable<'a> for SingleSubstFormat1<'a> {
    fn type_name(&self) -> &str {
        "SingleSubstFormat1"
    }
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
        match idx {
            0usize => Some(Field::new("subst_format", self.subst_format())),
            1usize => Some(Field::new(
                "coverage_offset",
                FieldType::offset(self.coverage_offset(), self.coverage()),
            )),
            2usize => Some(Field::new("delta_glyph_id", self.delta_glyph_id())),
            _ => None,
        }
    }
}

#[cfg(feature = "experimental_traverse")]
#[allow(clippy::needless_lifetimes)]
impl<'a> std::fmt::Debug for SingleSubstFormat1<'a> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        (self as &dyn SomeTable<'a>).fmt(f)
    }
}

impl Format<u16> for SingleSubstFormat2<'_> {
    const FORMAT: u16 = 2;
}

impl<'a> MinByteRange<'a> for SingleSubstFormat2<'a> {
    fn min_byte_range(&self) -> Range<usize> {
        0..self.substitute_glyph_ids_byte_range().end
    }
    fn min_table_bytes(&self) -> &'a [u8] {
        let range = self.min_byte_range();
        self.data.as_bytes().get(range).unwrap_or_default()
    }
}

impl<'a> FontRead<'a> for SingleSubstFormat2<'a> {
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
        #[allow(clippy::absurd_extreme_comparisons)]
        if data.len() < Self::MIN_SIZE {
            return Err(ReadError::OutOfBounds);
        }
        Ok(Self { data })
    }
}

/// [Single Substitution Format 2](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#12-single-substitution-format-2)
#[derive(Clone)]
pub struct SingleSubstFormat2<'a> {
    data: FontData<'a>,
}

#[allow(clippy::needless_lifetimes)]
impl<'a> SingleSubstFormat2<'a> {
    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
    basic_table_impls!(impl_the_methods);

    /// Format identifier: format = 2
    pub fn subst_format(&self) -> u16 {
        let range = self.subst_format_byte_range();
        self.data.read_at(range.start).ok().unwrap()
    }

    /// Offset to Coverage table, from beginning of substitution
    /// subtable
    pub fn coverage_offset(&self) -> Offset16 {
        let range = self.coverage_offset_byte_range();
        self.data.read_at(range.start).ok().unwrap()
    }

    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
        let data = self.data;
        self.coverage_offset().resolve(data)
    }

    /// Number of glyph IDs in the substituteGlyphIDs array
    pub fn glyph_count(&self) -> u16 {
        let range = self.glyph_count_byte_range();
        self.data.read_at(range.start).ok().unwrap()
    }

    /// Array of substitute glyph IDs — ordered by Coverage index
    pub fn substitute_glyph_ids(&self) -> &'a [BigEndian<GlyphId16>] {
        let range = self.substitute_glyph_ids_byte_range();
        self.data.read_array(range).ok().unwrap_or_default()
    }

    pub fn subst_format_byte_range(&self) -> Range<usize> {
        let start = 0;
        start..start + u16::RAW_BYTE_LEN
    }

    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
        let start = self.subst_format_byte_range().end;
        start..start + Offset16::RAW_BYTE_LEN
    }

    pub fn glyph_count_byte_range(&self) -> Range<usize> {
        let start = self.coverage_offset_byte_range().end;
        start..start + u16::RAW_BYTE_LEN
    }

    pub fn substitute_glyph_ids_byte_range(&self) -> Range<usize> {
        let glyph_count = self.glyph_count();
        let start = self.glyph_count_byte_range().end;
        start..start + (glyph_count as usize).saturating_mul(GlyphId16::RAW_BYTE_LEN)
    }
}

#[cfg(feature = "experimental_traverse")]
impl<'a> SomeTable<'a> for SingleSubstFormat2<'a> {
    fn type_name(&self) -> &str {
        "SingleSubstFormat2"
    }
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
        match idx {
            0usize => Some(Field::new("subst_format", self.subst_format())),
            1usize => Some(Field::new(
                "coverage_offset",
                FieldType::offset(self.coverage_offset(), self.coverage()),
            )),
            2usize => Some(Field::new("glyph_count", self.glyph_count())),
            3usize => Some(Field::new(
                "substitute_glyph_ids",
                self.substitute_glyph_ids(),
            )),
            _ => None,
        }
    }
}

#[cfg(feature = "experimental_traverse")]
#[allow(clippy::needless_lifetimes)]
impl<'a> std::fmt::Debug for SingleSubstFormat2<'a> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        (self as &dyn SomeTable<'a>).fmt(f)
    }
}

impl Format<u16> for MultipleSubstFormat1<'_> {
    const FORMAT: u16 = 1;
}

impl<'a> MinByteRange<'a> for MultipleSubstFormat1<'a> {
    fn min_byte_range(&self) -> Range<usize> {
        0..self.sequence_offsets_byte_range().end
    }
    fn min_table_bytes(&self) -> &'a [u8] {
        let range = self.min_byte_range();
        self.data.as_bytes().get(range).unwrap_or_default()
    }
}

impl<'a> FontRead<'a> for MultipleSubstFormat1<'a> {
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
        #[allow(clippy::absurd_extreme_comparisons)]
        if data.len() < Self::MIN_SIZE {
            return Err(ReadError::OutOfBounds);
        }
        Ok(Self { data })
    }
}

/// [Multiple Substitution Format 1](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#21-multiple-substitution-format-1)
#[derive(Clone)]
pub struct MultipleSubstFormat1<'a> {
    data: FontData<'a>,
}

#[allow(clippy::needless_lifetimes)]
impl<'a> MultipleSubstFormat1<'a> {
    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
    basic_table_impls!(impl_the_methods);

    /// Format identifier: format = 1
    pub fn subst_format(&self) -> u16 {
        let range = self.subst_format_byte_range();
        self.data.read_at(range.start).ok().unwrap()
    }

    /// Offset to Coverage table, from beginning of substitution
    /// subtable
    pub fn coverage_offset(&self) -> Offset16 {
        let range = self.coverage_offset_byte_range();
        self.data.read_at(range.start).ok().unwrap()
    }

    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
        let data = self.data;
        self.coverage_offset().resolve(data)
    }

    /// Number of Sequence table offsets in the sequenceOffsets array
    pub fn sequence_count(&self) -> u16 {
        let range = self.sequence_count_byte_range();
        self.data.read_at(range.start).ok().unwrap()
    }

    /// Array of offsets to Sequence tables. Offsets are from beginning
    /// of substitution subtable, ordered by Coverage index
    pub fn sequence_offsets(&self) -> &'a [BigEndian<Offset16>] {
        let range = self.sequence_offsets_byte_range();
        self.data.read_array(range).ok().unwrap_or_default()
    }

    /// A dynamically resolving wrapper for [`sequence_offsets`][Self::sequence_offsets].
    pub fn sequences(&self) -> ArrayOfOffsets<'a, Sequence<'a>, Offset16> {
        let data = self.data;
        let offsets = self.sequence_offsets();
        ArrayOfOffsets::new(offsets, data, ())
    }

    pub fn subst_format_byte_range(&self) -> Range<usize> {
        let start = 0;
        start..start + u16::RAW_BYTE_LEN
    }

    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
        let start = self.subst_format_byte_range().end;
        start..start + Offset16::RAW_BYTE_LEN
    }

    pub fn sequence_count_byte_range(&self) -> Range<usize> {
        let start = self.coverage_offset_byte_range().end;
        start..start + u16::RAW_BYTE_LEN
    }

    pub fn sequence_offsets_byte_range(&self) -> Range<usize> {
        let sequence_count = self.sequence_count();
        let start = self.sequence_count_byte_range().end;
        start..start + (sequence_count as usize).saturating_mul(Offset16::RAW_BYTE_LEN)
    }
}

const _: () = assert!(FontData::default_data_long_enough(
    MultipleSubstFormat1::MIN_SIZE
));

impl Default for MultipleSubstFormat1<'_> {
    fn default() -> Self {
        Self {
            data: FontData::default_format_1_u16_table_data(),
        }
    }
}

#[cfg(feature = "experimental_traverse")]
impl<'a> SomeTable<'a> for MultipleSubstFormat1<'a> {
    fn type_name(&self) -> &str {
        "MultipleSubstFormat1"
    }
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
        match idx {
            0usize => Some(Field::new("subst_format", self.subst_format())),
            1usize => Some(Field::new(
                "coverage_offset",
                FieldType::offset(self.coverage_offset(), self.coverage()),
            )),
            2usize => Some(Field::new("sequence_count", self.sequence_count())),
            3usize => Some(Field::new(
                "sequence_offsets",
                FieldType::from(self.sequences()),
            )),
            _ => None,
        }
    }
}

#[cfg(feature = "experimental_traverse")]
#[allow(clippy::needless_lifetimes)]
impl<'a> std::fmt::Debug for MultipleSubstFormat1<'a> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        (self as &dyn SomeTable<'a>).fmt(f)
    }
}

impl<'a> MinByteRange<'a> for Sequence<'a> {
    fn min_byte_range(&self) -> Range<usize> {
        0..self.substitute_glyph_ids_byte_range().end
    }
    fn min_table_bytes(&self) -> &'a [u8] {
        let range = self.min_byte_range();
        self.data.as_bytes().get(range).unwrap_or_default()
    }
}

impl<'a> FontRead<'a> for Sequence<'a> {
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
        #[allow(clippy::absurd_extreme_comparisons)]
        if data.len() < Self::MIN_SIZE {
            return Err(ReadError::OutOfBounds);
        }
        Ok(Self { data })
    }
}

/// Part of [MultipleSubstFormat1]
#[derive(Clone)]
pub struct Sequence<'a> {
    data: FontData<'a>,
}

#[allow(clippy::needless_lifetimes)]
impl<'a> Sequence<'a> {
    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
    basic_table_impls!(impl_the_methods);

    /// Number of glyph IDs in the substituteGlyphIDs array. This must
    /// always be greater than 0.
    pub fn glyph_count(&self) -> u16 {
        let range = self.glyph_count_byte_range();
        self.data.read_at(range.start).ok().unwrap()
    }

    /// String of glyph IDs to substitute
    pub fn substitute_glyph_ids(&self) -> &'a [BigEndian<GlyphId16>] {
        let range = self.substitute_glyph_ids_byte_range();
        self.data.read_array(range).ok().unwrap_or_default()
    }

    pub fn glyph_count_byte_range(&self) -> Range<usize> {
        let start = 0;
        start..start + u16::RAW_BYTE_LEN
    }

    pub fn substitute_glyph_ids_byte_range(&self) -> Range<usize> {
        let glyph_count = self.glyph_count();
        let start = self.glyph_count_byte_range().end;
        start..start + (glyph_count as usize).saturating_mul(GlyphId16::RAW_BYTE_LEN)
    }
}

const _: () = assert!(FontData::default_data_long_enough(Sequence::MIN_SIZE));

impl Default for Sequence<'_> {
    fn default() -> Self {
        Self {
            data: FontData::default_table_data(),
        }
    }
}

#[cfg(feature = "experimental_traverse")]
impl<'a> SomeTable<'a> for Sequence<'a> {
    fn type_name(&self) -> &str {
        "Sequence"
    }
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
        match idx {
            0usize => Some(Field::new("glyph_count", self.glyph_count())),
            1usize => Some(Field::new(
                "substitute_glyph_ids",
                self.substitute_glyph_ids(),
            )),
            _ => None,
        }
    }
}

#[cfg(feature = "experimental_traverse")]
#[allow(clippy::needless_lifetimes)]
impl<'a> std::fmt::Debug for Sequence<'a> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        (self as &dyn SomeTable<'a>).fmt(f)
    }
}

impl Format<u16> for AlternateSubstFormat1<'_> {
    const FORMAT: u16 = 1;
}

impl<'a> MinByteRange<'a> for AlternateSubstFormat1<'a> {
    fn min_byte_range(&self) -> Range<usize> {
        0..self.alternate_set_offsets_byte_range().end
    }
    fn min_table_bytes(&self) -> &'a [u8] {
        let range = self.min_byte_range();
        self.data.as_bytes().get(range).unwrap_or_default()
    }
}

impl<'a> FontRead<'a> for AlternateSubstFormat1<'a> {
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
        #[allow(clippy::absurd_extreme_comparisons)]
        if data.len() < Self::MIN_SIZE {
            return Err(ReadError::OutOfBounds);
        }
        Ok(Self { data })
    }
}

/// [Alternate Substitution Format 1](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#31-alternate-substitution-format-1)
#[derive(Clone)]
pub struct AlternateSubstFormat1<'a> {
    data: FontData<'a>,
}

#[allow(clippy::needless_lifetimes)]
impl<'a> AlternateSubstFormat1<'a> {
    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
    basic_table_impls!(impl_the_methods);

    /// Format identifier: format = 1
    pub fn subst_format(&self) -> u16 {
        let range = self.subst_format_byte_range();
        self.data.read_at(range.start).ok().unwrap()
    }

    /// Offset to Coverage table, from beginning of substitution
    /// subtable
    pub fn coverage_offset(&self) -> Offset16 {
        let range = self.coverage_offset_byte_range();
        self.data.read_at(range.start).ok().unwrap()
    }

    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
        let data = self.data;
        self.coverage_offset().resolve(data)
    }

    /// Number of AlternateSet tables
    pub fn alternate_set_count(&self) -> u16 {
        let range = self.alternate_set_count_byte_range();
        self.data.read_at(range.start).ok().unwrap()
    }

    /// Array of offsets to AlternateSet tables. Offsets are from
    /// beginning of substitution subtable, ordered by Coverage index
    pub fn alternate_set_offsets(&self) -> &'a [BigEndian<Offset16>] {
        let range = self.alternate_set_offsets_byte_range();
        self.data.read_array(range).ok().unwrap_or_default()
    }

    /// A dynamically resolving wrapper for [`alternate_set_offsets`][Self::alternate_set_offsets].
    pub fn alternate_sets(&self) -> ArrayOfOffsets<'a, AlternateSet<'a>, Offset16> {
        let data = self.data;
        let offsets = self.alternate_set_offsets();
        ArrayOfOffsets::new(offsets, data, ())
    }

    pub fn subst_format_byte_range(&self) -> Range<usize> {
        let start = 0;
        start..start + u16::RAW_BYTE_LEN
    }

    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
        let start = self.subst_format_byte_range().end;
        start..start + Offset16::RAW_BYTE_LEN
    }

    pub fn alternate_set_count_byte_range(&self) -> Range<usize> {
        let start = self.coverage_offset_byte_range().end;
        start..start + u16::RAW_BYTE_LEN
    }

    pub fn alternate_set_offsets_byte_range(&self) -> Range<usize> {
        let alternate_set_count = self.alternate_set_count();
        let start = self.alternate_set_count_byte_range().end;
        start..start + (alternate_set_count as usize).saturating_mul(Offset16::RAW_BYTE_LEN)
    }
}

const _: () = assert!(FontData::default_data_long_enough(
    AlternateSubstFormat1::MIN_SIZE
));

impl Default for AlternateSubstFormat1<'_> {
    fn default() -> Self {
        Self {
            data: FontData::default_format_1_u16_table_data(),
        }
    }
}

#[cfg(feature = "experimental_traverse")]
impl<'a> SomeTable<'a> for AlternateSubstFormat1<'a> {
    fn type_name(&self) -> &str {
        "AlternateSubstFormat1"
    }
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
        match idx {
            0usize => Some(Field::new("subst_format", self.subst_format())),
            1usize => Some(Field::new(
                "coverage_offset",
                FieldType::offset(self.coverage_offset(), self.coverage()),
            )),
            2usize => Some(Field::new(
                "alternate_set_count",
                self.alternate_set_count(),
            )),
            3usize => Some(Field::new(
                "alternate_set_offsets",
                FieldType::from(self.alternate_sets()),
            )),
            _ => None,
        }
    }
}

#[cfg(feature = "experimental_traverse")]
#[allow(clippy::needless_lifetimes)]
impl<'a> std::fmt::Debug for AlternateSubstFormat1<'a> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        (self as &dyn SomeTable<'a>).fmt(f)
    }
}

impl<'a> MinByteRange<'a> for AlternateSet<'a> {
    fn min_byte_range(&self) -> Range<usize> {
        0..self.alternate_glyph_ids_byte_range().end
    }
    fn min_table_bytes(&self) -> &'a [u8] {
        let range = self.min_byte_range();
        self.data.as_bytes().get(range).unwrap_or_default()
    }
}

impl<'a> FontRead<'a> for AlternateSet<'a> {
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
        #[allow(clippy::absurd_extreme_comparisons)]
        if data.len() < Self::MIN_SIZE {
            return Err(ReadError::OutOfBounds);
        }
        Ok(Self { data })
    }
}

/// Part of [AlternateSubstFormat1]
#[derive(Clone)]
pub struct AlternateSet<'a> {
    data: FontData<'a>,
}

#[allow(clippy::needless_lifetimes)]
impl<'a> AlternateSet<'a> {
    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
    basic_table_impls!(impl_the_methods);

    /// Number of glyph IDs in the alternateGlyphIDs array
    pub fn glyph_count(&self) -> u16 {
        let range = self.glyph_count_byte_range();
        self.data.read_at(range.start).ok().unwrap()
    }

    /// Array of alternate glyph IDs, in arbitrary order
    pub fn alternate_glyph_ids(&self) -> &'a [BigEndian<GlyphId16>] {
        let range = self.alternate_glyph_ids_byte_range();
        self.data.read_array(range).ok().unwrap_or_default()
    }

    pub fn glyph_count_byte_range(&self) -> Range<usize> {
        let start = 0;
        start..start + u16::RAW_BYTE_LEN
    }

    pub fn alternate_glyph_ids_byte_range(&self) -> Range<usize> {
        let glyph_count = self.glyph_count();
        let start = self.glyph_count_byte_range().end;
        start..start + (glyph_count as usize).saturating_mul(GlyphId16::RAW_BYTE_LEN)
    }
}

const _: () = assert!(FontData::default_data_long_enough(AlternateSet::MIN_SIZE));

impl Default for AlternateSet<'_> {
    fn default() -> Self {
        Self {
            data: FontData::default_table_data(),
        }
    }
}

#[cfg(feature = "experimental_traverse")]
impl<'a> SomeTable<'a> for AlternateSet<'a> {
    fn type_name(&self) -> &str {
        "AlternateSet"
    }
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
        match idx {
            0usize => Some(Field::new("glyph_count", self.glyph_count())),
            1usize => Some(Field::new(
                "alternate_glyph_ids",
                self.alternate_glyph_ids(),
            )),
            _ => None,
        }
    }
}

#[cfg(feature = "experimental_traverse")]
#[allow(clippy::needless_lifetimes)]
impl<'a> std::fmt::Debug for AlternateSet<'a> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        (self as &dyn SomeTable<'a>).fmt(f)
    }
}

impl Format<u16> for LigatureSubstFormat1<'_> {
    const FORMAT: u16 = 1;
}

impl<'a> MinByteRange<'a> for LigatureSubstFormat1<'a> {
    fn min_byte_range(&self) -> Range<usize> {
        0..self.ligature_set_offsets_byte_range().end
    }
    fn min_table_bytes(&self) -> &'a [u8] {
        let range = self.min_byte_range();
        self.data.as_bytes().get(range).unwrap_or_default()
    }
}

impl<'a> FontRead<'a> for LigatureSubstFormat1<'a> {
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
        #[allow(clippy::absurd_extreme_comparisons)]
        if data.len() < Self::MIN_SIZE {
            return Err(ReadError::OutOfBounds);
        }
        Ok(Self { data })
    }
}

/// [Ligature Substitution Format 1](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#41-ligature-substitution-format-1)
#[derive(Clone)]
pub struct LigatureSubstFormat1<'a> {
    data: FontData<'a>,
}

#[allow(clippy::needless_lifetimes)]
impl<'a> LigatureSubstFormat1<'a> {
    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
    basic_table_impls!(impl_the_methods);

    /// Format identifier: format = 1
    pub fn subst_format(&self) -> u16 {
        let range = self.subst_format_byte_range();
        self.data.read_at(range.start).ok().unwrap()
    }

    /// Offset to Coverage table, from beginning of substitution
    /// subtable
    pub fn coverage_offset(&self) -> Offset16 {
        let range = self.coverage_offset_byte_range();
        self.data.read_at(range.start).ok().unwrap()
    }

    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
        let data = self.data;
        self.coverage_offset().resolve(data)
    }

    /// Number of LigatureSet tables
    pub fn ligature_set_count(&self) -> u16 {
        let range = self.ligature_set_count_byte_range();
        self.data.read_at(range.start).ok().unwrap()
    }

    /// Array of offsets to LigatureSet tables. Offsets are from
    /// beginning of substitution subtable, ordered by Coverage index
    pub fn ligature_set_offsets(&self) -> &'a [BigEndian<Offset16>] {
        let range = self.ligature_set_offsets_byte_range();
        self.data.read_array(range).ok().unwrap_or_default()
    }

    /// A dynamically resolving wrapper for [`ligature_set_offsets`][Self::ligature_set_offsets].
    pub fn ligature_sets(&self) -> ArrayOfOffsets<'a, LigatureSet<'a>, Offset16> {
        let data = self.data;
        let offsets = self.ligature_set_offsets();
        ArrayOfOffsets::new(offsets, data, ())
    }

    pub fn subst_format_byte_range(&self) -> Range<usize> {
        let start = 0;
        start..start + u16::RAW_BYTE_LEN
    }

    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
        let start = self.subst_format_byte_range().end;
        start..start + Offset16::RAW_BYTE_LEN
    }

    pub fn ligature_set_count_byte_range(&self) -> Range<usize> {
        let start = self.coverage_offset_byte_range().end;
        start..start + u16::RAW_BYTE_LEN
    }

    pub fn ligature_set_offsets_byte_range(&self) -> Range<usize> {
        let ligature_set_count = self.ligature_set_count();
        let start = self.ligature_set_count_byte_range().end;
        start..start + (ligature_set_count as usize).saturating_mul(Offset16::RAW_BYTE_LEN)
    }
}

const _: () = assert!(FontData::default_data_long_enough(
    LigatureSubstFormat1::MIN_SIZE
));

impl Default for LigatureSubstFormat1<'_> {
    fn default() -> Self {
        Self {
            data: FontData::default_format_1_u16_table_data(),
        }
    }
}

#[cfg(feature = "experimental_traverse")]
impl<'a> SomeTable<'a> for LigatureSubstFormat1<'a> {
    fn type_name(&self) -> &str {
        "LigatureSubstFormat1"
    }
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
        match idx {
            0usize => Some(Field::new("subst_format", self.subst_format())),
            1usize => Some(Field::new(
                "coverage_offset",
                FieldType::offset(self.coverage_offset(), self.coverage()),
            )),
            2usize => Some(Field::new("ligature_set_count", self.ligature_set_count())),
            3usize => Some(Field::new(
                "ligature_set_offsets",
                FieldType::from(self.ligature_sets()),
            )),
            _ => None,
        }
    }
}

#[cfg(feature = "experimental_traverse")]
#[allow(clippy::needless_lifetimes)]
impl<'a> std::fmt::Debug for LigatureSubstFormat1<'a> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        (self as &dyn SomeTable<'a>).fmt(f)
    }
}

impl<'a> MinByteRange<'a> for LigatureSet<'a> {
    fn min_byte_range(&self) -> Range<usize> {
        0..self.ligature_offsets_byte_range().end
    }
    fn min_table_bytes(&self) -> &'a [u8] {
        let range = self.min_byte_range();
        self.data.as_bytes().get(range).unwrap_or_default()
    }
}

impl<'a> FontRead<'a> for LigatureSet<'a> {
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
        #[allow(clippy::absurd_extreme_comparisons)]
        if data.len() < Self::MIN_SIZE {
            return Err(ReadError::OutOfBounds);
        }
        Ok(Self { data })
    }
}

/// Part of [LigatureSubstFormat1]
#[derive(Clone)]
pub struct LigatureSet<'a> {
    data: FontData<'a>,
}

#[allow(clippy::needless_lifetimes)]
impl<'a> LigatureSet<'a> {
    pub const MIN_SIZE: usize = u16::RAW_BYTE_LEN;
    basic_table_impls!(impl_the_methods);

    /// Number of Ligature tables
    pub fn ligature_count(&self) -> u16 {
        let range = self.ligature_count_byte_range();
        self.data.read_at(range.start).ok().unwrap()
    }

    /// Array of offsets to Ligature tables. Offsets are from beginning
    /// of LigatureSet table, ordered by preference.
    pub fn ligature_offsets(&self) -> &'a [BigEndian<Offset16>] {
        let range = self.ligature_offsets_byte_range();
        self.data.read_array(range).ok().unwrap_or_default()
    }

    /// A dynamically resolving wrapper for [`ligature_offsets`][Self::ligature_offsets].
    pub fn ligatures(&self) -> ArrayOfOffsets<'a, Ligature<'a>, Offset16> {
        let data = self.data;
        let offsets = self.ligature_offsets();
        ArrayOfOffsets::new(offsets, data, ())
    }

    pub fn ligature_count_byte_range(&self) -> Range<usize> {
        let start = 0;
        start..start + u16::RAW_BYTE_LEN
    }

    pub fn ligature_offsets_byte_range(&self) -> Range<usize> {
        let ligature_count = self.ligature_count();
        let start = self.ligature_count_byte_range().end;
        start..start + (ligature_count as usize).saturating_mul(Offset16::RAW_BYTE_LEN)
    }
}

const _: () = assert!(FontData::default_data_long_enough(LigatureSet::MIN_SIZE));

impl Default for LigatureSet<'_> {
    fn default() -> Self {
        Self {
            data: FontData::default_table_data(),
        }
    }
}

#[cfg(feature = "experimental_traverse")]
impl<'a> SomeTable<'a> for LigatureSet<'a> {
    fn type_name(&self) -> &str {
        "LigatureSet"
    }
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
        match idx {
            0usize => Some(Field::new("ligature_count", self.ligature_count())),
            1usize => Some(Field::new(
                "ligature_offsets",
                FieldType::from(self.ligatures()),
            )),
            _ => None,
        }
    }
}

#[cfg(feature = "experimental_traverse")]
#[allow(clippy::needless_lifetimes)]
impl<'a> std::fmt::Debug for LigatureSet<'a> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        (self as &dyn SomeTable<'a>).fmt(f)
    }
}

impl<'a> MinByteRange<'a> for Ligature<'a> {
    fn min_byte_range(&self) -> Range<usize> {
        0..self.component_glyph_ids_byte_range().end
    }
    fn min_table_bytes(&self) -> &'a [u8] {
        let range = self.min_byte_range();
        self.data.as_bytes().get(range).unwrap_or_default()
    }
}

impl<'a> FontRead<'a> for Ligature<'a> {
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
        #[allow(clippy::absurd_extreme_comparisons)]
        if data.len() < Self::MIN_SIZE {
            return Err(ReadError::OutOfBounds);
        }
        Ok(Self { data })
    }
}

/// Part of [LigatureSubstFormat1]
#[derive(Clone)]
pub struct Ligature<'a> {
    data: FontData<'a>,
}

#[allow(clippy::needless_lifetimes)]
impl<'a> Ligature<'a> {
    pub const MIN_SIZE: usize = (GlyphId16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN);
    basic_table_impls!(impl_the_methods);

    /// glyph ID of ligature to substitute
    pub fn ligature_glyph(&self) -> GlyphId16 {
        let range = self.ligature_glyph_byte_range();
        self.data.read_at(range.start).ok().unwrap()
    }

    /// Number of components in the ligature
    pub fn component_count(&self) -> u16 {
        let range = self.component_count_byte_range();
        self.data.read_at(range.start).ok().unwrap()
    }

    /// Array of component glyph IDs — start with the second
    /// component, ordered in writing direction
    pub fn component_glyph_ids(&self) -> &'a [BigEndian<GlyphId16>] {
        let range = self.component_glyph_ids_byte_range();
        self.data.read_array(range).ok().unwrap_or_default()
    }

    pub fn ligature_glyph_byte_range(&self) -> Range<usize> {
        let start = 0;
        start..start + GlyphId16::RAW_BYTE_LEN
    }

    pub fn component_count_byte_range(&self) -> Range<usize> {
        let start = self.ligature_glyph_byte_range().end;
        start..start + u16::RAW_BYTE_LEN
    }

    pub fn component_glyph_ids_byte_range(&self) -> Range<usize> {
        let component_count = self.component_count();
        let start = self.component_count_byte_range().end;
        start
            ..start
                + (transforms::subtract(component_count, 1_usize))
                    .saturating_mul(GlyphId16::RAW_BYTE_LEN)
    }
}

const _: () = assert!(FontData::default_data_long_enough(Ligature::MIN_SIZE));

impl Default for Ligature<'_> {
    fn default() -> Self {
        Self {
            data: FontData::default_table_data(),
        }
    }
}

#[cfg(feature = "experimental_traverse")]
impl<'a> SomeTable<'a> for Ligature<'a> {
    fn type_name(&self) -> &str {
        "Ligature"
    }
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
        match idx {
            0usize => Some(Field::new("ligature_glyph", self.ligature_glyph())),
            1usize => Some(Field::new("component_count", self.component_count())),
            2usize => Some(Field::new(
                "component_glyph_ids",
                self.component_glyph_ids(),
            )),
            _ => None,
        }
    }
}

#[cfg(feature = "experimental_traverse")]
#[allow(clippy::needless_lifetimes)]
impl<'a> std::fmt::Debug for Ligature<'a> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        (self as &dyn SomeTable<'a>).fmt(f)
    }
}

impl Format<u16> for ExtensionSubstFormat1<'_> {
    const FORMAT: u16 = 1;
}

impl Discriminant for ExtensionSubstFormat1<'_, ()> {
    fn read_discriminant(data: FontData<'_>) -> Result<u16, ReadError> {
        data.read_at(u16::RAW_BYTE_LEN)
    }
}

impl<'a, T> MinByteRange<'a> for ExtensionSubstFormat1<'a, T> {
    fn min_byte_range(&self) -> Range<usize> {
        0..self.extension_offset_byte_range().end
    }
    fn min_table_bytes(&self) -> &'a [u8] {
        let range = self.min_byte_range();
        self.data.as_bytes().get(range).unwrap_or_default()
    }
}

impl<'a, T> FontRead<'a> for ExtensionSubstFormat1<'a, T> {
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
        #[allow(clippy::absurd_extreme_comparisons)]
        if data.len() < Self::MIN_SIZE {
            return Err(ReadError::OutOfBounds);
        }
        Ok(Self {
            data,
            offset_type: std::marker::PhantomData,
        })
    }
}

impl<'a, T> ExtensionSubstFormat1<'a, T> {
    #[allow(dead_code)]
    /// Replace the specific generic type on this implementation with `()`
    pub(crate) fn of_unit_type(&self) -> ExtensionSubstFormat1<'a, ()> {
        ExtensionSubstFormat1 {
            data: self.data,
            offset_type: std::marker::PhantomData,
        }
    }
}

/// [Extension Substitution Subtable Format 1](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#71-extension-substitution-subtable-format-1)
#[derive(Clone)]
pub struct ExtensionSubstFormat1<'a, T = ()> {
    data: FontData<'a>,
    offset_type: std::marker::PhantomData<*const T>,
}

#[allow(clippy::needless_lifetimes)]
impl<'a, T> ExtensionSubstFormat1<'a, T> {
    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN + Offset32::RAW_BYTE_LEN);
    basic_table_impls!(impl_the_methods);

    /// Format identifier. Set to 1.
    pub fn subst_format(&self) -> u16 {
        let range = self.subst_format_byte_range();
        self.data.read_at(range.start).ok().unwrap()
    }

    /// Lookup type of subtable referenced by extensionOffset (that is,
    /// the extension subtable).
    pub fn extension_lookup_type(&self) -> u16 {
        let range = self.extension_lookup_type_byte_range();
        self.data.read_at(range.start).ok().unwrap()
    }

    /// Offset to the extension subtable, of lookup type
    /// extensionLookupType, relative to the start of the
    /// ExtensionSubstFormat1 subtable.
    pub fn extension_offset(&self) -> Offset32 {
        let range = self.extension_offset_byte_range();
        self.data.read_at(range.start).ok().unwrap()
    }

    /// Attempt to resolve [`extension_offset`][Self::extension_offset].
    pub fn extension(&self) -> Result<T, ReadError>
    where
        T: FontRead<'a>,
    {
        let data = self.data;
        self.extension_offset().resolve(data)
    }

    pub fn subst_format_byte_range(&self) -> Range<usize> {
        let start = 0;
        start..start + u16::RAW_BYTE_LEN
    }

    pub fn extension_lookup_type_byte_range(&self) -> Range<usize> {
        let start = self.subst_format_byte_range().end;
        start..start + u16::RAW_BYTE_LEN
    }

    pub fn extension_offset_byte_range(&self) -> Range<usize> {
        let start = self.extension_lookup_type_byte_range().end;
        start..start + Offset32::RAW_BYTE_LEN
    }
}

const _: () = assert!(FontData::default_data_long_enough(
    ExtensionSubstFormat1::<()>::MIN_SIZE
));

impl<T> Default for ExtensionSubstFormat1<'_, T> {
    fn default() -> Self {
        Self {
            data: FontData::default_format_1_u16_table_data(),
            offset_type: std::marker::PhantomData,
        }
    }
}

#[cfg(feature = "experimental_traverse")]
impl<'a, T: FontRead<'a> + SomeTable<'a> + 'a> SomeTable<'a> for ExtensionSubstFormat1<'a, T> {
    fn type_name(&self) -> &str {
        "ExtensionSubstFormat1"
    }
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
        match idx {
            0usize => Some(Field::new("subst_format", self.subst_format())),
            1usize => Some(Field::new(
                "extension_lookup_type",
                self.extension_lookup_type(),
            )),
            2usize => Some(Field::new(
                "extension_offset",
                FieldType::offset(self.extension_offset(), self.extension()),
            )),
            _ => None,
        }
    }
}

#[cfg(feature = "experimental_traverse")]
#[allow(clippy::needless_lifetimes)]
impl<'a, T: FontRead<'a> + SomeTable<'a> + 'a> std::fmt::Debug for ExtensionSubstFormat1<'a, T> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        (self as &dyn SomeTable<'a>).fmt(f)
    }
}

/// A [GSUB Extension Substitution](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#ES) subtable
pub enum ExtensionSubtable<'a> {
    Single(ExtensionSubstFormat1<'a, SingleSubst<'a>>),
    Multiple(ExtensionSubstFormat1<'a, MultipleSubstFormat1<'a>>),
    Alternate(ExtensionSubstFormat1<'a, AlternateSubstFormat1<'a>>),
    Ligature(ExtensionSubstFormat1<'a, LigatureSubstFormat1<'a>>),
    Contextual(ExtensionSubstFormat1<'a, SubstitutionSequenceContext<'a>>),
    ChainContextual(ExtensionSubstFormat1<'a, SubstitutionChainContext<'a>>),
    Reverse(ExtensionSubstFormat1<'a, ReverseChainSingleSubstFormat1<'a>>),
}

impl Default for ExtensionSubtable<'_> {
    fn default() -> Self {
        Self::Single(Default::default())
    }
}

impl<'a> FontRead<'a> for ExtensionSubtable<'a> {
    fn read(bytes: FontData<'a>) -> Result<Self, ReadError> {
        let discriminant = ExtensionSubstFormat1::read_discriminant(bytes)?;
        match discriminant {
            1 => Ok(ExtensionSubtable::Single(FontRead::read(bytes)?)),
            2 => Ok(ExtensionSubtable::Multiple(FontRead::read(bytes)?)),
            3 => Ok(ExtensionSubtable::Alternate(FontRead::read(bytes)?)),
            4 => Ok(ExtensionSubtable::Ligature(FontRead::read(bytes)?)),
            5 => Ok(ExtensionSubtable::Contextual(FontRead::read(bytes)?)),
            6 => Ok(ExtensionSubtable::ChainContextual(FontRead::read(bytes)?)),
            8 => Ok(ExtensionSubtable::Reverse(FontRead::read(bytes)?)),
            other => Err(ReadError::InvalidFormat(other.into())),
        }
    }
}

impl<'a> ExtensionSubtable<'a> {
    #[allow(dead_code)]
    /// Return the inner table, removing the specific generics.
    ///
    /// This lets us return a single concrete type we can call methods on.
    pub(crate) fn of_unit_type(&self) -> ExtensionSubstFormat1<'a, ()> {
        match self {
            ExtensionSubtable::Single(inner) => inner.of_unit_type(),
            ExtensionSubtable::Multiple(inner) => inner.of_unit_type(),
            ExtensionSubtable::Alternate(inner) => inner.of_unit_type(),
            ExtensionSubtable::Ligature(inner) => inner.of_unit_type(),
            ExtensionSubtable::Contextual(inner) => inner.of_unit_type(),
            ExtensionSubtable::ChainContextual(inner) => inner.of_unit_type(),
            ExtensionSubtable::Reverse(inner) => inner.of_unit_type(),
        }
    }
}

#[cfg(feature = "experimental_traverse")]
impl<'a> ExtensionSubtable<'a> {
    fn dyn_inner(&self) -> &(dyn SomeTable<'a> + 'a) {
        match self {
            ExtensionSubtable::Single(table) => table,
            ExtensionSubtable::Multiple(table) => table,
            ExtensionSubtable::Alternate(table) => table,
            ExtensionSubtable::Ligature(table) => table,
            ExtensionSubtable::Contextual(table) => table,
            ExtensionSubtable::ChainContextual(table) => table,
            ExtensionSubtable::Reverse(table) => table,
        }
    }
}

#[cfg(feature = "experimental_traverse")]
impl<'a> SomeTable<'a> for ExtensionSubtable<'a> {
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
        self.dyn_inner().get_field(idx)
    }
    fn type_name(&self) -> &str {
        self.dyn_inner().type_name()
    }
}

#[cfg(feature = "experimental_traverse")]
impl std::fmt::Debug for ExtensionSubtable<'_> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.dyn_inner().fmt(f)
    }
}

impl Format<u16> for ReverseChainSingleSubstFormat1<'_> {
    const FORMAT: u16 = 1;
}

impl<'a> MinByteRange<'a> for ReverseChainSingleSubstFormat1<'a> {
    fn min_byte_range(&self) -> Range<usize> {
        0..self.substitute_glyph_ids_byte_range().end
    }
    fn min_table_bytes(&self) -> &'a [u8] {
        let range = self.min_byte_range();
        self.data.as_bytes().get(range).unwrap_or_default()
    }
}

impl<'a> FontRead<'a> for ReverseChainSingleSubstFormat1<'a> {
    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
        #[allow(clippy::absurd_extreme_comparisons)]
        if data.len() < Self::MIN_SIZE {
            return Err(ReadError::OutOfBounds);
        }
        Ok(Self { data })
    }
}

/// [Reverse Chaining Contextual Single Substitution Format 1](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#81-reverse-chaining-contextual-single-substitution-format-1-coverage-based-glyph-contexts)
#[derive(Clone)]
pub struct ReverseChainSingleSubstFormat1<'a> {
    data: FontData<'a>,
}

#[allow(clippy::needless_lifetimes)]
impl<'a> ReverseChainSingleSubstFormat1<'a> {
    pub const MIN_SIZE: usize = (u16::RAW_BYTE_LEN
        + Offset16::RAW_BYTE_LEN
        + u16::RAW_BYTE_LEN
        + u16::RAW_BYTE_LEN
        + u16::RAW_BYTE_LEN);
    basic_table_impls!(impl_the_methods);

    /// Format identifier: format = 1
    pub fn subst_format(&self) -> u16 {
        let range = self.subst_format_byte_range();
        self.data.read_at(range.start).ok().unwrap()
    }

    /// Offset to Coverage table, from beginning of substitution
    /// subtable.
    pub fn coverage_offset(&self) -> Offset16 {
        let range = self.coverage_offset_byte_range();
        self.data.read_at(range.start).ok().unwrap()
    }

    /// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
    pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
        let data = self.data;
        self.coverage_offset().resolve(data)
    }

    /// Number of glyphs in the backtrack sequence.
    pub fn backtrack_glyph_count(&self) -> u16 {
        let range = self.backtrack_glyph_count_byte_range();
        self.data.read_at(range.start).ok().unwrap()
    }

    /// Array of offsets to coverage tables in backtrack sequence, in
    /// glyph sequence order.
    pub fn backtrack_coverage_offsets(&self) -> &'a [BigEndian<Offset16>] {
        let range = self.backtrack_coverage_offsets_byte_range();
        self.data.read_array(range).ok().unwrap_or_default()
    }

    /// A dynamically resolving wrapper for [`backtrack_coverage_offsets`][Self::backtrack_coverage_offsets].
    pub fn backtrack_coverages(&self) -> ArrayOfOffsets<'a, CoverageTable<'a>, Offset16> {
        let data = self.data;
        let offsets = self.backtrack_coverage_offsets();
        ArrayOfOffsets::new(offsets, data, ())
    }

    /// Number of glyphs in lookahead sequence.
    pub fn lookahead_glyph_count(&self) -> u16 {
        let range = self.lookahead_glyph_count_byte_range();
        self.data.read_at(range.start).ok().unwrap_or_default()
    }

    /// Array of offsets to coverage tables in lookahead sequence, in
    /// glyph sequence order.
    pub fn lookahead_coverage_offsets(&self) -> &'a [BigEndian<Offset16>] {
        let range = self.lookahead_coverage_offsets_byte_range();
        self.data.read_array(range).ok().unwrap_or_default()
    }

    /// A dynamically resolving wrapper for [`lookahead_coverage_offsets`][Self::lookahead_coverage_offsets].
    pub fn lookahead_coverages(&self) -> ArrayOfOffsets<'a, CoverageTable<'a>, Offset16> {
        let data = self.data;
        let offsets = self.lookahead_coverage_offsets();
        ArrayOfOffsets::new(offsets, data, ())
    }

    /// Number of glyph IDs in the substituteGlyphIDs array.
    pub fn glyph_count(&self) -> u16 {
        let range = self.glyph_count_byte_range();
        self.data.read_at(range.start).ok().unwrap_or_default()
    }

    /// Array of substitute glyph IDs — ordered by Coverage index.
    pub fn substitute_glyph_ids(&self) -> &'a [BigEndian<GlyphId16>] {
        let range = self.substitute_glyph_ids_byte_range();
        self.data.read_array(range).ok().unwrap_or_default()
    }

    pub fn subst_format_byte_range(&self) -> Range<usize> {
        let start = 0;
        start..start + u16::RAW_BYTE_LEN
    }

    pub fn coverage_offset_byte_range(&self) -> Range<usize> {
        let start = self.subst_format_byte_range().end;
        start..start + Offset16::RAW_BYTE_LEN
    }

    pub fn backtrack_glyph_count_byte_range(&self) -> Range<usize> {
        let start = self.coverage_offset_byte_range().end;
        start..start + u16::RAW_BYTE_LEN
    }

    pub fn backtrack_coverage_offsets_byte_range(&self) -> Range<usize> {
        let backtrack_glyph_count = self.backtrack_glyph_count();
        let start = self.backtrack_glyph_count_byte_range().end;
        start..start + (backtrack_glyph_count as usize).saturating_mul(Offset16::RAW_BYTE_LEN)
    }

    pub fn lookahead_glyph_count_byte_range(&self) -> Range<usize> {
        let start = self.backtrack_coverage_offsets_byte_range().end;
        start..start + u16::RAW_BYTE_LEN
    }

    pub fn lookahead_coverage_offsets_byte_range(&self) -> Range<usize> {
        let lookahead_glyph_count = self.lookahead_glyph_count();
        let start = self.lookahead_glyph_count_byte_range().end;
        start..start + (lookahead_glyph_count as usize).saturating_mul(Offset16::RAW_BYTE_LEN)
    }

    pub fn glyph_count_byte_range(&self) -> Range<usize> {
        let start = self.lookahead_coverage_offsets_byte_range().end;
        start..start + u16::RAW_BYTE_LEN
    }

    pub fn substitute_glyph_ids_byte_range(&self) -> Range<usize> {
        let glyph_count = self.glyph_count();
        let start = self.glyph_count_byte_range().end;
        start..start + (glyph_count as usize).saturating_mul(GlyphId16::RAW_BYTE_LEN)
    }
}

const _: () = assert!(FontData::default_data_long_enough(
    ReverseChainSingleSubstFormat1::MIN_SIZE
));

impl Default for ReverseChainSingleSubstFormat1<'_> {
    fn default() -> Self {
        Self {
            data: FontData::default_format_1_u16_table_data(),
        }
    }
}

#[cfg(feature = "experimental_traverse")]
impl<'a> SomeTable<'a> for ReverseChainSingleSubstFormat1<'a> {
    fn type_name(&self) -> &str {
        "ReverseChainSingleSubstFormat1"
    }
    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
        match idx {
            0usize => Some(Field::new("subst_format", self.subst_format())),
            1usize => Some(Field::new(
                "coverage_offset",
                FieldType::offset(self.coverage_offset(), self.coverage()),
            )),
            2usize => Some(Field::new(
                "backtrack_glyph_count",
                self.backtrack_glyph_count(),
            )),
            3usize => Some(Field::new(
                "backtrack_coverage_offsets",
                FieldType::from(self.backtrack_coverages()),
            )),
            4usize => Some(Field::new(
                "lookahead_glyph_count",
                self.lookahead_glyph_count(),
            )),
            5usize => Some(Field::new(
                "lookahead_coverage_offsets",
                FieldType::from(self.lookahead_coverages()),
            )),
            6usize => Some(Field::new("glyph_count", self.glyph_count())),
            7usize => Some(Field::new(
                "substitute_glyph_ids",
                self.substitute_glyph_ids(),
            )),
            _ => None,
        }
    }
}

#[cfg(feature = "experimental_traverse")]
#[allow(clippy::needless_lifetimes)]
impl<'a> std::fmt::Debug for ReverseChainSingleSubstFormat1<'a> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        (self as &dyn SomeTable<'a>).fmt(f)
    }
}
