Skip to content

Commit

Permalink
[codegen] Add wrappers on enums for shared methods
Browse files Browse the repository at this point in the history
This is for tables that have multiple formats, where all the formats
have an identically named method; we will now generate a wrapper method
on the enum itself that forwards to the inner methods.

In this patch this is only implemented for raw fields, and we do not
generate the typed offset getters. This is quite a bit more complicated,
and we can look into that as followup.
  • Loading branch information
cmyr committed Jan 19, 2024
1 parent 5143ffa commit ba89609
Show file tree
Hide file tree
Showing 19 changed files with 554 additions and 49 deletions.
17 changes: 10 additions & 7 deletions font-codegen/src/fields.rs
Original file line number Diff line number Diff line change
Expand Up @@ -685,21 +685,24 @@ impl Field {
type_tokens
}

pub(crate) fn table_getter(&self, generic: Option<&syn::Ident>) -> Option<TokenStream> {
pub(crate) fn table_getter_return_type(&self) -> Option<TokenStream> {
if !self.has_getter() {
return None;
}

let return_type = self.raw_getter_return_type();
if self.is_version_dependent() {
Some(quote!(Option<#return_type>))
} else {
Some(return_type)
}
}
pub(crate) fn table_getter(&self, generic: Option<&syn::Ident>) -> Option<TokenStream> {
let return_type = self.table_getter_return_type()?;
let name = &self.name;
let is_array = self.is_array();
let is_var_array = self.is_var_array();
let is_versioned = self.is_version_dependent();

let mut return_type = self.raw_getter_return_type();
if is_versioned {
return_type = quote!(Option<#return_type>);
}

let range_stmt = self.getter_range_stmt();
let mut read_stmt = if let Some(args) = &self.attrs.read_with_args {
let get_args = args.to_tokens_for_table_getter();
Expand Down
2 changes: 1 addition & 1 deletion font-codegen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ pub(crate) fn generate_parse_module(items: &Items) -> Result<proc_macro2::TokenS
Item::Record(item) => record::generate(item, items)?,
Item::Table(item) => table::generate(item)?,
Item::GenericGroup(item) => table::generate_group(item)?,
Item::Format(item) => table::generate_format_group(item)?,
Item::Format(item) => table::generate_format_group(item, items)?,
Item::RawEnum(item) => flags_enums::generate_raw_enum(item),
Item::Flags(item) => flags_enums::generate_flags(item),
Item::Extern(..) => Default::default(),
Expand Down
23 changes: 20 additions & 3 deletions font-codegen/src/parsing.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
//! raw parsing code

use std::{backtrace::Backtrace, collections::HashMap, fmt::Display, ops::Deref, str::FromStr};
use std::{
backtrace::Backtrace, collections::HashMap, fmt::Display, hash::Hash, ops::Deref, str::FromStr,
};

use font_types::Tag;
use indexmap::IndexMap;
Expand Down Expand Up @@ -326,7 +328,7 @@ impl InlineExpr {
}
}

#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub(crate) enum FieldType {
Offset {
typ: syn::Ident,
Expand All @@ -352,7 +354,7 @@ pub(crate) enum FieldType {
VarLenArray(CustomArray),
}

#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub(crate) enum OffsetTarget {
Table(syn::Ident),
Array(Box<FieldType>),
Expand All @@ -366,6 +368,21 @@ pub(crate) struct CustomArray {
lifetime: Option<syn::Lifetime>,
}

impl PartialEq for CustomArray {
fn eq(&self, other: &Self) -> bool {
self.inner == other.inner && self.lifetime == other.lifetime
}
}

impl Eq for CustomArray {}

impl Hash for CustomArray {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.inner.hash(state);
self.lifetime.hash(state);
}
}

impl CustomArray {
pub(crate) fn compile_type(&self) -> TokenStream {
let inner = &self.inner;
Expand Down
82 changes: 81 additions & 1 deletion font-codegen/src/table.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
//! codegen for table objects

use std::collections::HashMap;

use crate::{fields::FieldConstructorInfo, parsing::logged_syn_error};
use indexmap::IndexMap;
use proc_macro2::{Span, TokenStream};
use quote::{quote, ToTokens};

Expand Down Expand Up @@ -593,6 +596,72 @@ fn generate_format_constructors(item: &TableFormat, items: &Items) -> syn::Resul
})
}

fn generate_format_shared_getters(item: &TableFormat, items: &Items) -> syn::Result<TokenStream> {
// okay so we want to identify the getters that exist on all variants.
let all_variants = item
.variants
.iter()
.map(|var| {
let type_name = var.type_name();
match items.get(type_name) {
Some(Item::Table(item)) => Ok(item),
_ => Err(logged_syn_error(
type_name.span(),
"must be a table defined in this file",
)),
}
})
.collect::<Result<Vec<_>, _>>()?;
// okay so now we have all of the actual inner types, and we need to find which
// getters are shared between all of them
let mut field_counts = IndexMap::new();
let mut all_fields = HashMap::new();
for table in &all_variants {
for field in table.fields.iter().filter(|fld| fld.has_getter()) {
let key = (&field.name, &field.typ);
// we have to convert the tokens to a string to get hash/ord/etc
*field_counts.entry(key).or_insert(0usize) += 1;
all_fields.entry(&field.name).or_insert(field);
}
}

let shared_fields = field_counts
.into_iter()
.filter(|(_, count)| *count == all_variants.len())
.map(|((name, _), _)| all_fields.get(name).unwrap())
.collect::<Vec<_>>();

let getters = shared_fields
.iter()
.map(|fld| generate_format_getter_for_shared_field(item, fld));

// now we have a collection of fields present on all variants, and
// we need to actually generate the wrapping getter

Ok(quote!( #( #getters )* ))
}

fn generate_format_getter_for_shared_field(item: &TableFormat, field: &Field) -> TokenStream {
let docs = &field.attrs.docs;
let method_name = &field.name;
let return_type = field.table_getter_return_type();
let arms = item.variants.iter().map(|variant| {
let var_name = &variant.name;
quote!(Self::#var_name(item) => item.#method_name(), )
});

// but we also need to handle offset getters, and that's a pain

quote! {
#( #docs )*
pub fn #method_name(&self) -> #return_type {
match self {
#( #arms )*
}
}
}
}

fn generate_format_from_obj(
item: &TableFormat,
parse_module: &syn::Path,
Expand Down Expand Up @@ -628,7 +697,7 @@ fn generate_format_from_obj(
})
}

pub(crate) fn generate_format_group(item: &TableFormat) -> syn::Result<TokenStream> {
pub(crate) fn generate_format_group(item: &TableFormat, items: &Items) -> syn::Result<TokenStream> {
let name = &item.name;
let docs = &item.attrs.docs;
let variants = item
Expand Down Expand Up @@ -684,13 +753,24 @@ pub(crate) fn generate_format_group(item: &TableFormat) -> syn::Result<TokenStre
.map(|lit| lit.base10_parse::<usize>().unwrap())
.unwrap_or(0);

let getters = generate_format_shared_getters(item, items)?;
let getters = (!getters.is_empty()).then(|| {
quote! {
impl<'a> #name<'a> {
#getters
}
}
});

Ok(quote! {
#( #docs )*
#[derive(Clone)]
pub enum #name<'a> {
#( #variants ),*
}

#getters

impl<'a> FontRead<'a> for #name<'a> {
fn read(data: FontData<'a>) -> Result<Self, ReadError> {
let format: #format = data.read_at(#format_offset)?;
Expand Down
20 changes: 20 additions & 0 deletions read-fonts/generated/generated_base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -932,6 +932,26 @@ pub enum BaseCoord<'a> {
Format3(BaseCoordFormat3<'a>),
}

impl<'a> BaseCoord<'a> {
/// Format identifier — format = 1
pub fn base_coord_format(&self) -> u16 {
match self {
Self::Format1(item) => item.base_coord_format(),
Self::Format2(item) => item.base_coord_format(),
Self::Format3(item) => item.base_coord_format(),
}
}

/// X or Y value, in design units
pub fn coordinate(&self) -> i16 {
match self {
Self::Format1(item) => item.coordinate(),
Self::Format2(item) => item.coordinate(),
Self::Format3(item) => item.coordinate(),
}
}
}

impl<'a> FontRead<'a> for BaseCoord<'a> {
fn read(data: FontData<'a>) -> Result<Self, ReadError> {
let format: u16 = data.read_at(0usize)?;
Expand Down
35 changes: 35 additions & 0 deletions read-fonts/generated/generated_bitmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -851,6 +851,41 @@ pub enum IndexSubtable<'a> {
Format5(IndexSubtable5<'a>),
}

impl<'a> IndexSubtable<'a> {
/// Format of this IndexSubTable.
pub fn index_format(&self) -> u16 {
match self {
Self::Format1(item) => item.index_format(),
Self::Format2(item) => item.index_format(),
Self::Format3(item) => item.index_format(),
Self::Format4(item) => item.index_format(),
Self::Format5(item) => item.index_format(),
}
}

/// Format of EBDT image data.
pub fn image_format(&self) -> u16 {
match self {
Self::Format1(item) => item.image_format(),
Self::Format2(item) => item.image_format(),
Self::Format3(item) => item.image_format(),
Self::Format4(item) => item.image_format(),
Self::Format5(item) => item.image_format(),
}
}

/// Offset to image data in EBDT table.
pub fn image_data_offset(&self) -> u32 {
match self {
Self::Format1(item) => item.image_data_offset(),
Self::Format2(item) => item.image_data_offset(),
Self::Format3(item) => item.image_data_offset(),
Self::Format4(item) => item.image_data_offset(),
Self::Format5(item) => item.image_data_offset(),
}
}
}

impl<'a> FontRead<'a> for IndexSubtable<'a> {
fn read(data: FontData<'a>) -> Result<Self, ReadError> {
let format: u16 = data.read_at(0usize)?;
Expand Down
17 changes: 17 additions & 0 deletions read-fonts/generated/generated_cmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,23 @@ pub enum CmapSubtable<'a> {
Format14(Cmap14<'a>),
}

impl<'a> CmapSubtable<'a> {
/// Format number is set to 0.
pub fn format(&self) -> u16 {
match self {
Self::Format0(item) => item.format(),
Self::Format2(item) => item.format(),
Self::Format4(item) => item.format(),
Self::Format6(item) => item.format(),
Self::Format8(item) => item.format(),
Self::Format10(item) => item.format(),
Self::Format12(item) => item.format(),
Self::Format13(item) => item.format(),
Self::Format14(item) => item.format(),
}
}
}

impl<'a> FontRead<'a> for CmapSubtable<'a> {
fn read(data: FontData<'a>) -> Result<Self, ReadError> {
let format: u16 = data.read_at(0usize)?;
Expand Down
82 changes: 82 additions & 0 deletions read-fonts/generated/generated_colr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -785,6 +785,48 @@ pub enum ClipBox<'a> {
Format2(ClipBoxFormat2<'a>),
}

impl<'a> ClipBox<'a> {
/// Set to 1.
pub fn format(&self) -> u8 {
match self {
Self::Format1(item) => item.format(),
Self::Format2(item) => item.format(),
}
}

/// Minimum x of clip box.
pub fn x_min(&self) -> FWord {
match self {
Self::Format1(item) => item.x_min(),
Self::Format2(item) => item.x_min(),
}
}

/// Minimum y of clip box.
pub fn y_min(&self) -> FWord {
match self {
Self::Format1(item) => item.y_min(),
Self::Format2(item) => item.y_min(),
}
}

/// Maximum x of clip box.
pub fn x_max(&self) -> FWord {
match self {
Self::Format1(item) => item.x_max(),
Self::Format2(item) => item.x_max(),
}
}

/// Maximum y of clip box.
pub fn y_max(&self) -> FWord {
match self {
Self::Format1(item) => item.y_max(),
Self::Format2(item) => item.y_max(),
}
}
}

impl<'a> FontRead<'a> for ClipBox<'a> {
fn read(data: FontData<'a>) -> Result<Self, ReadError> {
let format: u8 = data.read_at(0usize)?;
Expand Down Expand Up @@ -1525,6 +1567,46 @@ pub enum Paint<'a> {
Composite(PaintComposite<'a>),
}

impl<'a> Paint<'a> {
/// Set to 1.
pub fn format(&self) -> u8 {
match self {
Self::ColrLayers(item) => item.format(),
Self::Solid(item) => item.format(),
Self::VarSolid(item) => item.format(),
Self::LinearGradient(item) => item.format(),
Self::VarLinearGradient(item) => item.format(),
Self::RadialGradient(item) => item.format(),
Self::VarRadialGradient(item) => item.format(),
Self::SweepGradient(item) => item.format(),
Self::VarSweepGradient(item) => item.format(),
Self::Glyph(item) => item.format(),
Self::ColrGlyph(item) => item.format(),
Self::Transform(item) => item.format(),
Self::VarTransform(item) => item.format(),
Self::Translate(item) => item.format(),
Self::VarTranslate(item) => item.format(),
Self::Scale(item) => item.format(),
Self::VarScale(item) => item.format(),
Self::ScaleAroundCenter(item) => item.format(),
Self::VarScaleAroundCenter(item) => item.format(),
Self::ScaleUniform(item) => item.format(),
Self::VarScaleUniform(item) => item.format(),
Self::ScaleUniformAroundCenter(item) => item.format(),
Self::VarScaleUniformAroundCenter(item) => item.format(),
Self::Rotate(item) => item.format(),
Self::VarRotate(item) => item.format(),
Self::RotateAroundCenter(item) => item.format(),
Self::VarRotateAroundCenter(item) => item.format(),
Self::Skew(item) => item.format(),
Self::VarSkew(item) => item.format(),
Self::SkewAroundCenter(item) => item.format(),
Self::VarSkewAroundCenter(item) => item.format(),
Self::Composite(item) => item.format(),
}
}
}

impl<'a> FontRead<'a> for Paint<'a> {
fn read(data: FontData<'a>) -> Result<Self, ReadError> {
let format: u8 = data.read_at(0usize)?;
Expand Down
Loading

0 comments on commit ba89609

Please sign in to comment.