read_fonts/
traversal.rs

1//! Experimental generic traversal of font tables.
2//!
3//! This module defines functionality that allows untyped access to font table
4//! data. This is used as the basis for things like debug printing.
5//!
6//! The basis of traversal is the [`SomeTable`] trait, which is implemented for
7//! all font tables. This trait provides the table's name, as well as ordered access
8//! to the table's fields. Using this, it is possible to iterate through a table
9//! and its subtables, records, and values.
10//!
11//! # Warning
12//!
13//! This functionality is considered experimental, and the API may break or be
14//! removed without warning.
15
16use std::{fmt::Debug, ops::Deref};
17
18use types::{
19    BigEndian, F2Dot14, FWord, Fixed, GlyphId16, Int24, LongDateTime, MajorMinor, NameId, Nullable,
20    Offset16, Offset24, Offset32, Scalar, Tag, UfWord, Uint24, Version16Dot16,
21};
22
23use crate::{
24    array::{ComputedArray, VarLenArray},
25    read::{ComputeSize, ReadArgs},
26    FontData, FontRead, FontReadWithArgs, ReadError, VarSize,
27};
28
29/// Types of fields in font tables.
30///
31/// Fields can either be scalars, offsets to tables, or arrays.
32pub enum FieldType<'a> {
33    I8(i8),
34    U8(u8),
35    I16(i16),
36    U16(u16),
37    I32(i32),
38    U32(u32),
39    I24(Int24),
40    U24(Uint24),
41    Tag(Tag),
42    FWord(FWord),
43    UfWord(UfWord),
44    MajorMinor(MajorMinor),
45    Version16Dot16(Version16Dot16),
46    F2Dot14(F2Dot14),
47    Fixed(Fixed),
48    LongDateTime(LongDateTime),
49    GlyphId16(GlyphId16),
50    NameId(NameId),
51    BareOffset(OffsetType),
52    ResolvedOffset(ResolvedOffset<'a>),
53    /// Used in tables like name/post so we can actually print the strings
54    StringOffset(StringOffset<'a>),
55    /// Used in COLR/CPAL
56    ArrayOffset(ArrayOffset<'a>),
57    Record(RecordResolver<'a>),
58    Array(Box<dyn SomeArray<'a> + 'a>),
59    Unknown,
60}
61
62/// Any offset type.
63#[derive(Clone, Copy)]
64pub enum OffsetType {
65    Offset16(u16),
66    Offset24(Uint24),
67    Offset32(u32),
68}
69
70impl OffsetType {
71    /// Return this offset as a u32.
72    pub fn to_u32(self) -> u32 {
73        match self {
74            Self::Offset16(val) => val.into(),
75            Self::Offset24(val) => val.into(),
76            Self::Offset32(val) => val,
77        }
78    }
79}
80
81/// An offset, as well as the table it references.
82pub struct ResolvedOffset<'a> {
83    /// The raw offset
84    pub offset: OffsetType,
85    /// The parsed table pointed to by this offset, or an error if parsing fails.
86    pub target: Result<Box<dyn SomeTable<'a> + 'a>, ReadError>,
87}
88
89/// An offset to string data.
90///
91/// This is a special case for the name table (and maybe elsewhere?)
92pub struct StringOffset<'a> {
93    pub offset: OffsetType,
94    pub target: Result<Box<dyn SomeString<'a> + 'a>, ReadError>,
95}
96
97/// An offset to an array.
98pub struct ArrayOffset<'a> {
99    pub offset: OffsetType,
100    pub target: Result<Box<dyn SomeArray<'a> + 'a>, ReadError>,
101}
102
103pub(crate) struct ArrayOfOffsets<'a, O> {
104    type_name: &'static str,
105    offsets: &'a [O],
106    resolver: Box<dyn Fn(&O) -> FieldType<'a> + 'a>,
107}
108
109impl<'a, O> SomeArray<'a> for ArrayOfOffsets<'a, O> {
110    fn type_name(&self) -> &str {
111        self.type_name
112    }
113
114    fn len(&self) -> usize {
115        self.offsets.len()
116    }
117
118    fn get(&self, idx: usize) -> Option<FieldType<'a>> {
119        let off = self.offsets.get(idx)?;
120        let target = (self.resolver)(off);
121        Some(target)
122    }
123}
124
125impl<'a> FieldType<'a> {
126    /// makes a field, handling the case where this array may not be present in
127    /// all versions
128    pub fn array_of_records<T>(
129        type_name: &'static str,
130        records: &'a [T],
131        data: FontData<'a>,
132    ) -> FieldType<'a>
133    where
134        T: Clone + SomeRecord<'a> + 'a,
135    {
136        ArrayOfRecords {
137            type_name,
138            data,
139            records,
140        }
141        .into()
142    }
143
144    // Convenience method for handling computed arrays
145    pub fn computed_array<T>(
146        type_name: &'static str,
147        array: ComputedArray<'a, T>,
148        data: FontData<'a>,
149    ) -> FieldType<'a>
150    where
151        T: FontReadWithArgs<'a> + ComputeSize + SomeRecord<'a> + 'a,
152        T::Args: Copy + 'static,
153    {
154        ComputedArrayOfRecords {
155            type_name,
156            data,
157            array,
158        }
159        .into()
160    }
161
162    // Convenience method for handling VarLenArrays
163    pub fn var_array<T>(
164        type_name: &'static str,
165        array: VarLenArray<'a, T>,
166        data: FontData<'a>,
167    ) -> FieldType<'a>
168    where
169        T: FontRead<'a> + VarSize + SomeRecord<'a> + 'a,
170    {
171        VarLenArrayOfRecords {
172            type_name,
173            data,
174            array,
175        }
176        .into()
177    }
178
179    /// Convenience method for creating a `FieldType` from an array of offsets.
180    ///
181    /// The `resolver` argument is a function that takes an offset and resolves
182    /// it.
183    pub fn array_of_offsets<O>(
184        type_name: &'static str,
185        offsets: &'a [O],
186        resolver: impl Fn(&O) -> FieldType<'a> + 'a,
187    ) -> Self
188where {
189        FieldType::Array(Box::new(ArrayOfOffsets {
190            type_name,
191            offsets,
192            resolver: Box::new(resolver),
193        }))
194    }
195
196    /// Convenience method for creating a `FieldType` from an offset to an array.
197    pub fn offset_to_array_of_scalars<T: SomeArray<'a> + 'a>(
198        offset: impl Into<OffsetType>,
199        result: impl Into<Option<Result<T, ReadError>>>,
200    ) -> Self {
201        let offset = offset.into();
202        match result.into() {
203            Some(target) => FieldType::ArrayOffset(ArrayOffset {
204                offset,
205                target: target.map(|x| Box::new(x) as Box<dyn SomeArray>),
206            }),
207            None => FieldType::BareOffset(offset),
208        }
209    }
210
211    /// Convenience method for creating a `FieldType` from an offset to an array.
212    pub fn offset_to_array_of_records<T: Clone + SomeRecord<'a> + 'a>(
213        offset: impl Into<OffsetType>,
214        result: impl Into<Option<Result<&'a [T], ReadError>>>,
215        type_name: &'static str,
216        data: FontData<'a>,
217    ) -> Self {
218        let offset = offset.into();
219        match result.into() {
220            Some(target) => {
221                let target = target.map(|records| {
222                    Box::new(ArrayOfRecords {
223                        type_name,
224                        data,
225                        records,
226                    }) as Box<dyn SomeArray>
227                });
228                FieldType::ArrayOffset(ArrayOffset { offset, target })
229            }
230            None => FieldType::BareOffset(offset),
231        }
232    }
233
234    //FIXME: I bet this is generating a *lot* of code
235    /// Convenience method for creating a `FieldType` for a resolved offset.
236    ///
237    /// This handles cases where offsets are nullable, in which case the `result`
238    /// argument may be `None`.
239    pub fn offset<T: SomeTable<'a> + 'a>(
240        offset: impl Into<OffsetType>,
241        result: impl Into<Option<Result<T, ReadError>>>,
242    ) -> Self {
243        let offset = offset.into();
244        match result.into() {
245            Some(target) => FieldType::ResolvedOffset(ResolvedOffset {
246                offset,
247                target: target.map(|x| Box::new(x) as Box<dyn SomeTable>),
248            }),
249            None => FieldType::BareOffset(offset),
250        }
251    }
252
253    /// Convenience method for creating a `FieldType` from an unknown offset.
254    pub fn unknown_offset(offset: impl Into<OffsetType>) -> Self {
255        Self::BareOffset(offset.into())
256    }
257}
258
259/// A generic field in a font table.
260pub struct Field<'a> {
261    /// The field's name.
262    pub name: &'static str,
263    /// The field's value.
264    pub value: FieldType<'a>,
265}
266
267/// A generic table type.
268///
269/// This is intended to be used as a trait object, and is a way of generically
270/// representing any table, providing ordered access to that table's fields.
271pub trait SomeTable<'a> {
272    /// The name of this table
273    fn type_name(&self) -> &str;
274    /// Access this table's fields, in declaration order.
275    fn get_field(&self, idx: usize) -> Option<Field<'a>>;
276}
277
278impl<'a> dyn SomeTable<'a> + 'a {
279    /// Returns an iterator over this table's fields.
280    pub fn iter(&self) -> impl Iterator<Item = Field<'a>> + '_ {
281        FieldIter {
282            table: self,
283            idx: 0,
284        }
285    }
286}
287
288struct FieldIter<'a, 'b> {
289    table: &'b dyn SomeTable<'a>,
290    idx: usize,
291}
292
293impl<'a> Iterator for FieldIter<'a, '_> {
294    type Item = Field<'a>;
295
296    fn next(&mut self) -> Option<Self::Item> {
297        let this = self.idx;
298        self.idx += 1;
299        self.table.get_field(this)
300    }
301}
302
303impl<'a> SomeTable<'a> for Box<dyn SomeTable<'a> + 'a> {
304    fn type_name(&self) -> &str {
305        self.deref().type_name()
306    }
307
308    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
309        self.deref().get_field(idx)
310    }
311}
312
313/// A generic trait for records, which need to be passed in data
314/// in order to fully resolve themselves.
315pub trait SomeRecord<'a> {
316    fn traverse(self, data: FontData<'a>) -> RecordResolver<'a>;
317}
318
319/// A struct created from a record and the data it needs to resolve any
320/// contained offsets.
321pub struct RecordResolver<'a> {
322    pub(crate) name: &'static str,
323    pub(crate) get_field: Box<dyn Fn(usize, FontData<'a>) -> Option<Field<'a>> + 'a>,
324    pub(crate) data: FontData<'a>,
325}
326
327/// A generic trait for arrays.
328pub trait SomeArray<'a> {
329    /// The name of this type. For an array of u16s, this is `[u16]`.
330    fn type_name(&self) -> &str;
331
332    /// The length of the array.
333    fn len(&self) -> usize;
334
335    /// Returns `true` if this array is empty.
336    fn is_empty(&self) -> bool {
337        self.len() == 0
338    }
339
340    /// Return the item at `idx`, or `None` if `idx` is out of bounds.
341    fn get(&self, idx: usize) -> Option<FieldType<'a>>;
342}
343
344impl<'a> dyn SomeArray<'a> + 'a {
345    /// Return an iterator over the contents of this array.
346    pub fn iter(&self) -> impl Iterator<Item = FieldType<'a>> + '_ {
347        ArrayIter {
348            array: self,
349            idx: 0,
350        }
351    }
352}
353
354struct ArrayIter<'a, 'b> {
355    array: &'b dyn SomeArray<'a>,
356    idx: usize,
357}
358
359impl<'a> Iterator for ArrayIter<'a, '_> {
360    type Item = FieldType<'a>;
361
362    fn next(&mut self) -> Option<Self::Item> {
363        let this = self.idx;
364        self.idx += 1;
365        self.array.get(this)
366    }
367}
368
369impl<'a, T: Scalar + Into<FieldType<'a>>> SomeArray<'a> for &'a [BigEndian<T>]
370where
371    BigEndian<T>: Copy, // i don't know why i need this??
372{
373    fn len(&self) -> usize {
374        (*self).len()
375    }
376
377    fn get(&self, idx: usize) -> Option<FieldType<'a>> {
378        (*self).get(idx).map(|val| val.get().into())
379    }
380
381    fn type_name(&self) -> &str {
382        let full_name = std::any::type_name::<T>();
383        full_name.split("::").last().unwrap_or(full_name)
384    }
385}
386
387impl<'a> SomeArray<'a> for &'a [u8] {
388    fn type_name(&self) -> &str {
389        "u8"
390    }
391
392    fn len(&self) -> usize {
393        (*self).len()
394    }
395
396    fn get(&self, idx: usize) -> Option<FieldType<'a>> {
397        (*self).get(idx).copied().map(Into::into)
398    }
399}
400
401impl<'a> SomeArray<'a> for Box<dyn SomeArray<'a> + 'a> {
402    fn type_name(&self) -> &str {
403        self.deref().type_name()
404    }
405
406    fn len(&self) -> usize {
407        self.deref().len()
408    }
409
410    fn get(&self, idx: usize) -> Option<FieldType<'a>> {
411        self.deref().get(idx)
412    }
413}
414
415pub trait SomeString<'a> {
416    fn iter_chars(&self) -> Box<dyn Iterator<Item = char> + 'a>;
417}
418
419impl<'a> SomeString<'a> for Box<dyn SomeString<'a> + 'a> {
420    fn iter_chars(&self) -> Box<dyn Iterator<Item = char> + 'a> {
421        self.deref().iter_chars()
422    }
423}
424
425// only used as Box<dyn SomeArray<'a>>
426struct ArrayOfRecords<'a, T> {
427    pub(crate) type_name: &'static str,
428    pub(crate) data: FontData<'a>,
429    pub(crate) records: &'a [T],
430}
431
432// only used as Box<dyn SomeArray<'a>>
433struct ComputedArrayOfRecords<'a, T: ReadArgs> {
434    pub(crate) type_name: &'static str,
435    pub(crate) data: FontData<'a>,
436    pub(crate) array: ComputedArray<'a, T>,
437}
438
439struct VarLenArrayOfRecords<'a, T> {
440    pub(crate) type_name: &'static str,
441    pub(crate) data: FontData<'a>,
442    pub(crate) array: VarLenArray<'a, T>,
443}
444
445impl<'a, T> SomeArray<'a> for ComputedArrayOfRecords<'a, T>
446where
447    T: FontReadWithArgs<'a> + ComputeSize + SomeRecord<'a> + 'a,
448    T::Args: Copy + 'static,
449    Self: 'a,
450{
451    fn len(&self) -> usize {
452        self.array.len()
453    }
454
455    fn get(&self, idx: usize) -> Option<FieldType<'a>> {
456        self.array
457            .get(idx)
458            .ok()
459            .map(|record| record.traverse(self.data).into())
460    }
461
462    fn type_name(&self) -> &str {
463        self.type_name
464    }
465}
466
467impl<'a, T: SomeRecord<'a> + Clone> SomeArray<'a> for ArrayOfRecords<'a, T> {
468    fn type_name(&self) -> &str {
469        self.type_name
470    }
471
472    fn len(&self) -> usize {
473        self.records.len()
474    }
475
476    fn get(&self, idx: usize) -> Option<FieldType<'a>> {
477        self.records
478            .get(idx)
479            .map(|record| record.clone().traverse(self.data).into())
480    }
481}
482
483impl<'a, T> SomeArray<'a> for VarLenArrayOfRecords<'a, T>
484where
485    T: FontRead<'a> + VarSize + SomeRecord<'a> + 'a,
486    Self: 'a,
487{
488    fn len(&self) -> usize {
489        self.array.iter().count()
490    }
491
492    fn get(&self, idx: usize) -> Option<FieldType<'a>> {
493        self.array
494            .get(idx)?
495            .ok()
496            .map(|record| record.traverse(self.data).into())
497    }
498
499    fn type_name(&self) -> &str {
500        self.type_name
501    }
502}
503
504impl<'a> Field<'a> {
505    /// Create a new field with the given name and value.
506    pub fn new(name: &'static str, value: impl Into<FieldType<'a>>) -> Self {
507        Field {
508            name,
509            value: value.into(),
510        }
511    }
512}
513
514/// A wrapper type that implements `Debug` for any table.
515struct DebugPrintTable<'a, 'b>(pub &'b (dyn SomeTable<'a> + 'a));
516
517/// A wrapper type that implements `Debug` for any array.
518struct DebugPrintArray<'a, 'b>(pub &'b (dyn SomeArray<'a> + 'a));
519
520impl<'a> Debug for FieldType<'a> {
521    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
522        match self {
523            Self::I8(arg0) => arg0.fmt(f),
524            Self::U8(arg0) => arg0.fmt(f),
525            Self::I16(arg0) => arg0.fmt(f),
526            Self::U16(arg0) => arg0.fmt(f),
527            Self::I32(arg0) => arg0.fmt(f),
528            Self::U32(arg0) => arg0.fmt(f),
529            Self::I24(arg0) => arg0.fmt(f),
530            Self::U24(arg0) => arg0.fmt(f),
531            Self::Tag(arg0) => arg0.fmt(f),
532            Self::FWord(arg0) => arg0.to_i16().fmt(f),
533            Self::UfWord(arg0) => arg0.to_u16().fmt(f),
534            Self::MajorMinor(arg0) => write!(f, "{}.{}", arg0.major, arg0.minor),
535            Self::Version16Dot16(arg0) => arg0.fmt(f),
536            Self::F2Dot14(arg0) => arg0.fmt(f),
537            Self::Fixed(arg0) => arg0.fmt(f),
538            Self::LongDateTime(arg0) => arg0.as_secs().fmt(f),
539            Self::GlyphId16(arg0) => {
540                write!(f, "g")?;
541                arg0.to_u16().fmt(f)
542            }
543            Self::NameId(arg0) => arg0.fmt(f),
544            Self::StringOffset(string) => match &string.target {
545                Ok(arg0) => arg0.as_ref().fmt(f),
546                Err(_) => string.target.fmt(f),
547            },
548            Self::ArrayOffset(array) => match &array.target {
549                Ok(arg0) => arg0.as_ref().fmt(f),
550                Err(_) => array.target.fmt(f),
551            },
552            Self::BareOffset(arg0) => write!(f, "0x{:04X}", arg0.to_u32()),
553            Self::ResolvedOffset(ResolvedOffset {
554                target: Ok(arg0), ..
555            }) => arg0.fmt(f),
556            Self::ResolvedOffset(arg0) => arg0.target.fmt(f),
557            Self::Record(arg0) => (arg0 as &(dyn SomeTable<'a> + 'a)).fmt(f),
558            Self::Array(arg0) => arg0.fmt(f),
559            Self::Unknown => write!(f, "no repr available"),
560        }
561    }
562}
563
564impl std::fmt::Debug for DebugPrintTable<'_, '_> {
565    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
566        let mut debug_struct = f.debug_struct(self.0.type_name());
567        for field in self.0.iter() {
568            debug_struct.field(field.name, &field.value);
569        }
570        debug_struct.finish()
571    }
572}
573
574impl<'a> Debug for dyn SomeTable<'a> + 'a {
575    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
576        DebugPrintTable(self).fmt(f)
577    }
578}
579
580impl<'a> Debug for dyn SomeString<'a> + 'a {
581    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
582        write!(f, "\"")?;
583        for c in self.iter_chars() {
584            write!(f, "{c}")?
585        }
586        write!(f, "\"")
587    }
588}
589
590impl std::fmt::Debug for DebugPrintArray<'_, '_> {
591    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
592        let mut debug_list = f.debug_list();
593        let mut idx = 0;
594        while let Some(item) = self.0.get(idx) {
595            idx += 1;
596            debug_list.entry(&item);
597        }
598        debug_list.finish()
599    }
600}
601
602impl<'a> Debug for dyn SomeArray<'a> + 'a {
603    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
604        DebugPrintArray(self).fmt(f)
605    }
606}
607
608impl std::fmt::Display for OffsetType {
609    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
610        write!(f, "{:+}", self.to_u32())
611    }
612}
613
614// used to give us an auto-impl of Debug
615impl<'a> SomeTable<'a> for RecordResolver<'a> {
616    fn type_name(&self) -> &str {
617        self.name
618    }
619
620    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
621        (self.get_field)(idx, self.data)
622    }
623}
624
625impl<'a> From<u8> for FieldType<'a> {
626    fn from(src: u8) -> FieldType<'a> {
627        FieldType::U8(src)
628    }
629}
630
631impl<'a> From<i8> for FieldType<'a> {
632    fn from(src: i8) -> FieldType<'a> {
633        FieldType::I8(src)
634    }
635}
636
637impl<'a> From<u16> for FieldType<'a> {
638    fn from(src: u16) -> FieldType<'a> {
639        FieldType::U16(src)
640    }
641}
642
643impl<'a> From<i16> for FieldType<'a> {
644    fn from(src: i16) -> FieldType<'a> {
645        FieldType::I16(src)
646    }
647}
648
649impl<'a> From<u32> for FieldType<'a> {
650    fn from(src: u32) -> FieldType<'a> {
651        FieldType::U32(src)
652    }
653}
654
655impl<'a> From<i32> for FieldType<'a> {
656    fn from(src: i32) -> FieldType<'a> {
657        FieldType::I32(src)
658    }
659}
660
661impl<'a> From<Uint24> for FieldType<'a> {
662    fn from(src: Uint24) -> FieldType<'a> {
663        FieldType::U24(src)
664    }
665}
666
667impl<'a> From<Int24> for FieldType<'a> {
668    fn from(src: Int24) -> FieldType<'a> {
669        FieldType::I24(src)
670    }
671}
672
673impl<'a> From<Tag> for FieldType<'a> {
674    fn from(src: Tag) -> FieldType<'a> {
675        FieldType::Tag(src)
676    }
677}
678
679impl<'a> From<FWord> for FieldType<'a> {
680    fn from(src: FWord) -> FieldType<'a> {
681        FieldType::FWord(src)
682    }
683}
684
685impl<'a> From<UfWord> for FieldType<'a> {
686    fn from(src: UfWord) -> FieldType<'a> {
687        FieldType::UfWord(src)
688    }
689}
690
691impl<'a> From<Fixed> for FieldType<'a> {
692    fn from(src: Fixed) -> FieldType<'a> {
693        FieldType::Fixed(src)
694    }
695}
696
697impl<'a> From<F2Dot14> for FieldType<'a> {
698    fn from(src: F2Dot14) -> FieldType<'a> {
699        FieldType::F2Dot14(src)
700    }
701}
702
703impl<'a> From<LongDateTime> for FieldType<'a> {
704    fn from(src: LongDateTime) -> FieldType<'a> {
705        FieldType::LongDateTime(src)
706    }
707}
708
709impl<'a> From<MajorMinor> for FieldType<'a> {
710    fn from(src: MajorMinor) -> FieldType<'a> {
711        FieldType::MajorMinor(src)
712    }
713}
714
715impl<'a> From<Version16Dot16> for FieldType<'a> {
716    fn from(src: Version16Dot16) -> FieldType<'a> {
717        FieldType::Version16Dot16(src)
718    }
719}
720
721impl<'a> From<GlyphId16> for FieldType<'a> {
722    fn from(src: GlyphId16) -> FieldType<'a> {
723        FieldType::GlyphId16(src)
724    }
725}
726
727impl<'a> From<NameId> for FieldType<'a> {
728    fn from(src: NameId) -> FieldType<'a> {
729        FieldType::NameId(src)
730    }
731}
732
733impl<'a> From<RecordResolver<'a>> for FieldType<'a> {
734    fn from(src: RecordResolver<'a>) -> Self {
735        FieldType::Record(src)
736    }
737}
738
739impl<'a, T: SomeArray<'a> + 'a> From<T> for FieldType<'a> {
740    fn from(src: T) -> Self {
741        FieldType::Array(Box::new(src))
742    }
743}
744
745impl From<Offset16> for OffsetType {
746    fn from(src: Offset16) -> OffsetType {
747        OffsetType::Offset16(src.to_u32() as u16)
748    }
749}
750
751impl From<Offset24> for OffsetType {
752    fn from(src: Offset24) -> OffsetType {
753        OffsetType::Offset24(Uint24::new(src.to_u32()))
754    }
755}
756
757impl From<Offset32> for OffsetType {
758    fn from(src: Offset32) -> OffsetType {
759        OffsetType::Offset32(src.to_u32())
760    }
761}
762
763impl<'a> From<Offset16> for FieldType<'a> {
764    fn from(src: Offset16) -> FieldType<'a> {
765        FieldType::BareOffset(src.into())
766    }
767}
768
769impl<'a> From<Offset24> for FieldType<'a> {
770    fn from(src: Offset24) -> FieldType<'a> {
771        FieldType::BareOffset(src.into())
772    }
773}
774
775impl<'a> From<Offset32> for FieldType<'a> {
776    fn from(src: Offset32) -> FieldType<'a> {
777        FieldType::BareOffset(src.into())
778    }
779}
780
781impl<T: Into<OffsetType> + Clone> From<Nullable<T>> for OffsetType {
782    fn from(src: Nullable<T>) -> Self {
783        src.offset().clone().into()
784    }
785}