ttf_view/tables/
directory.rs1use crate::{
2 tables::{Table, TableError},
3 types::{Offset32, Tag, tags, uint16, uint32},
4 util::custom_iterator,
5};
6
7#[repr(C)]
8pub struct TableDirectory {
9 pub sfnt_version: uint32,
10 pub num_tables: uint16,
11 pub search_range: uint16,
12 pub entry_selector: uint16,
13 pub range_shift: uint16,
14 table_records: [TableRecordRaw; 0],
15}
16#[repr(C)]
17pub struct TableRecordRaw {
18 _exhaustive_but_dont_instantiate: (),
19 pub table_tag: Tag,
20 pub checksum: uint32,
21 pub offset: Offset32,
22 pub length: uint32,
23}
24
25impl TableDirectory {
26 pub fn new(bytes: &[u8]) -> Result<&Self, TableError> {
27 if bytes.len() < size_of::<TableDirectory>() {
29 return Err(TableError::InvalidLen);
30 }
31 let dir = unsafe { Self::new_unchecked(bytes) };
32
33 let required_len = size_of::<TableDirectory>()
35 + dir.num_tables.get() as usize * size_of::<TableRecordRaw>();
36 if bytes.len() < required_len {
37 return Err(TableError::InvalidLen);
38 }
39
40 for table in dir.table_records_raw() {
42 let offset = table.offset.get() as usize + table.length.get() as usize;
43 if bytes.len() < offset {
44 return Err(TableError::InvalidLen);
45 }
46 }
47
48 Ok(dir)
49 }
50 pub const unsafe fn new_unchecked(bytes: &[u8]) -> &Self {
51 unsafe { &*bytes.as_ptr().cast() }
52 }
53
54 pub const fn directory_as_bytes(&self) -> &[u8] {
55 let start = std::ptr::from_ref(self).cast();
56 let end = self.table_records_raw().as_ptr_range().end.cast();
57 unsafe { std::slice::from_ptr_range(start..end) }
58 }
59
60 pub const fn table_records_raw(&self) -> &[TableRecordRaw] {
61 let len = self.num_tables.get() as usize;
62 unsafe { std::slice::from_raw_parts(self.table_records.as_ptr(), len) }
63 }
64 pub fn table_record_raw(&self, tag: Tag) -> Option<&TableRecordRaw> {
65 self.table_records_raw().iter().find(|x| x.table_tag == tag)
66 }
67
68 pub const fn table_records(&self) -> TableRecordsIter<'_> {
69 TableRecordsIter::new(self)
70 }
71 pub fn table_record(&self, tag: Tag) -> Option<TableRecord<'_>> {
72 Some(TableRecord(self, self.table_record_raw(tag)?))
73 }
74
75 pub fn table<'a, T: Table<'a>>(&'a self) -> Result<T, TableError> {
76 T::new_in(self)
77 }
78
79 }
81
82#[derive(Copy)]
83#[derive_const(Clone)]
84pub struct TableRecord<'a>(&'a TableDirectory, &'a TableRecordRaw);
85
86const impl<'a> std::ops::Deref for TableRecord<'a> {
87 type Target = &'a TableRecordRaw;
88 fn deref(&self) -> &Self::Target {
89 &self.1
90 }
91}
92
93impl<'a> TableRecord<'a> {
94 pub const fn table_as_bytes(&self) -> &'a [u8] {
95 unsafe {
96 let start = std::ptr::from_ref(self.0).cast::<u8>().add(self.offset.get() as _);
97 std::slice::from_raw_parts(start, self.length.get() as _)
98 }
99 }
100 pub fn table_as<T: Table<'a>>(&self) -> Result<T, TableError> {
101 T::new_in(self.0)
102 }
103
104 pub(crate) fn raw_as<T>(&self) -> Option<&'a T> {
105 if self.length.get() >= size_of::<T>() as u32 {
106 Some(unsafe { &*self.table_as_bytes().as_ptr().cast() })
107 } else {
108 None
109 }
110 }
111
112 pub fn calculate_checksum(&self) -> u32 {
113 let (uint32s, unpadded) = self.table_as_bytes().as_chunks::<4>();
114 let mut sum: u32 = 0;
115
116 for chunk in uint32s {
117 sum = sum.wrapping_add(u32::from_be_bytes(*chunk));
118 }
119
120 if !unpadded.is_empty() {
121 let mut buf = [0; 4];
122 buf[..unpadded.len()].copy_from_slice(unpadded);
123 sum = sum.wrapping_add(u32::from_be_bytes(buf));
124 }
125
126 if self.table_tag == tags::head {
127 let checksum_adjustment = u32::from_be_bytes(uint32s[2]);
128 sum = sum.wrapping_sub(checksum_adjustment);
129 }
130
131 sum
132 }
133}
134
135#[derive(Clone)]
137pub struct TableRecordsIter<'a> {
138 dir: &'a TableDirectory,
139 inner: std::slice::Iter<'a, TableRecordRaw>,
140}
141impl<'a> TableRecordsIter<'a> {
142 pub const fn new(dir: &'a TableDirectory) -> Self {
143 Self { dir, inner: dir.table_records_raw().iter() }
144 }
145 pub fn as_records(&self) -> &'a [TableRecordRaw] {
147 self.inner.as_slice()
148 }
149}
150custom_iterator!(TableRecordsIter<'a> as this {
151 type Item = TableRecord<'a>;
152 map: |x| TableRecord(this.dir, x);
153});
154
155impl std::fmt::Debug for TableDirectory {
156 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
157 f.debug_struct("TableDirectory")
158 .field_with("sfnt_version", |f| write!(f, "{:#010X}", self.sfnt_version))
159 .field("num_tables", &self.num_tables.get())
160 .field("search_range", &self.search_range.get())
161 .field("entry_selector", &self.entry_selector.get())
162 .field("range_shift", &self.range_shift.get())
163 .field_with("table_records", |f| {
164 let mut list = f.debug_list();
165
166 for table in self.table_records() {
167 list.entry_with(|f| {
168 table.fmt(&mut f.with_options(*f.options().alternate(false)))
169 });
170 }
171 list.finish()
172 })
173 .finish()
174 }
175}
176impl std::fmt::Debug for TableRecordRaw {
177 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
178 f.debug_struct("TableRecord")
179 .field("table_tag", &self.table_tag)
180 .field_with("checksum", |f| write!(f, "{:#010X}", self.checksum))
181 .field_with("offset", |f| write!(f, "{:#010X}", self.offset))
182 .field_with("length", |f| write!(f, "{:#010X}", self.length))
183 .finish()
184 }
185}
186impl std::fmt::Debug for TableRecord<'_> {
187 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
188 TableRecordRaw::fmt(self, f)
189 }
190}