1use core::num::NonZeroU16;
8
9use crate::GlyphId;
10use crate::parser::{Stream, FromData, LazyArray16, Offset, Offset16, Offset32, NumFrom};
11
12pub mod state {
14 #![allow(missing_docs)]
15 pub const START_OF_TEXT: u16 = 0;
16}
17
18pub mod class {
23 #![allow(missing_docs)]
24 pub const END_OF_TEXT: u8 = 0;
25 pub const OUT_OF_BOUNDS: u8 = 1;
26 pub const DELETED_GLYPH: u8 = 2;
27}
28
29#[derive(Clone, Copy, Debug)]
33pub struct GenericStateEntry<T: FromData> {
34 pub new_state: u16,
36 pub flags: u16,
38 pub extra: T,
42}
43
44impl<T: FromData> FromData for GenericStateEntry<T> {
45 const SIZE: usize = 4 + T::SIZE;
46
47 #[inline]
48 fn parse(data: &[u8]) -> Option<Self> {
49 let mut s = Stream::new(data);
50 Some(GenericStateEntry {
51 new_state: s.read::<u16>()?,
52 flags: s.read::<u16>()?,
53 extra: s.read::<T>()?,
54 })
55 }
56}
57
58impl<T: FromData> GenericStateEntry<T> {
59 #[inline]
61 pub fn has_offset(&self) -> bool {
62 self.flags & 0x3FFF != 0
63 }
64
65 #[inline]
69 pub fn value_offset(&self) -> ValueOffset {
70 ValueOffset(self.flags & 0x3FFF)
71 }
72
73 #[inline]
75 pub fn has_reset(&self) -> bool {
76 self.flags & 0x2000 != 0
77 }
78
79 #[inline]
81 pub fn has_advance(&self) -> bool {
82 self.flags & 0x4000 == 0
83 }
84
85 #[inline]
87 pub fn has_push(&self) -> bool {
88 self.flags & 0x8000 != 0
89 }
90
91 #[inline]
97 pub fn has_mark(&self) -> bool {
98 self.flags & 0x8000 != 0
99 }
100}
101
102pub type StateEntry = GenericStateEntry<()>;
104
105#[derive(Clone, Copy, Debug)]
107pub struct ValueOffset(u16);
108
109impl ValueOffset {
110 #[inline]
114 pub fn next(self) -> Self {
115 ValueOffset(self.0.wrapping_add(u16::SIZE as u16))
116 }
117}
118
119#[derive(Clone)]
126pub struct StateTable<'a> {
127 number_of_classes: u16,
128 first_glyph: GlyphId,
129 class_table: &'a [u8],
130 state_array_offset: u16,
131 state_array: &'a [u8],
132 entry_table: &'a [u8],
133 actions: &'a [u8],
134}
135
136impl<'a> StateTable<'a> {
137 pub(crate) fn parse(data: &'a [u8]) -> Option<Self> {
138 let mut s = Stream::new(data);
139
140 let number_of_classes: u16 = s.read()?;
141 let class_table_offset = s.read::<Offset16>()?.to_usize();
145 let state_array_offset = s.read::<Offset16>()?.to_usize();
146 let entry_table_offset = s.read::<Offset16>()?.to_usize();
147 let mut s = Stream::new_at(data, class_table_offset)?;
151 let first_glyph: GlyphId = s.read()?;
152 let number_of_glyphs: u16 = s.read()?;
153 let class_table = s.read_bytes(usize::from(number_of_glyphs))?;
156
157 Some(StateTable {
158 number_of_classes,
159 first_glyph,
160 class_table,
161 state_array_offset: state_array_offset as u16,
162 state_array: data.get(state_array_offset..)?,
166 entry_table: data.get(entry_table_offset..)?,
167 actions: data,
170 })
171 }
172
173 #[inline]
175 pub fn class(&self, glyph_id: GlyphId) -> Option<u8> {
176 if glyph_id.0 == 0xFFFF {
177 return Some(class::DELETED_GLYPH as u8);
178 }
179
180 let idx = glyph_id.0.checked_sub(self.first_glyph.0)?;
181 self.class_table.get(usize::from(idx)).copied()
182 }
183
184 #[inline]
186 pub fn entry(&self, state: u16, mut class: u8) -> Option<StateEntry> {
187 if u16::from(class) >= self.number_of_classes {
188 class = class::OUT_OF_BOUNDS as u8;
189 }
190
191 let entry_idx = self.state_array.get(
192 usize::from(state) * usize::from(self.number_of_classes) + usize::from(class)
193 )?;
194
195 Stream::read_at(self.entry_table, usize::from(*entry_idx) * StateEntry::SIZE)
196 }
197
198 #[inline]
200 pub fn kerning(&self, offset: ValueOffset) -> Option<i16> {
201 Stream::read_at(self.actions, usize::from(offset.0))
202 }
203
204 #[inline]
206 pub fn new_state(&self, state: u16) -> u16 {
207 let n = (i32::from(state) - i32::from(self.state_array_offset))
208 / i32::from(self.number_of_classes);
209
210 use core::convert::TryFrom;
211 u16::try_from(n).unwrap_or(0)
212 }
213}
214
215impl core::fmt::Debug for StateTable<'_> {
216 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
217 write!(f, "StateTable {{ ... }}")
218 }
219}
220
221
222#[derive(Clone)]
229pub struct ExtendedStateTable<'a, T> {
230 number_of_classes: u32,
231 lookup: Lookup<'a>,
232 state_array: &'a [u8],
233 entry_table: &'a [u8],
234 entry_type: core::marker::PhantomData<T>,
235}
236
237impl<'a, T: FromData> ExtendedStateTable<'a, T> {
238 pub fn parse(number_of_glyphs: NonZeroU16, s: &mut Stream<'a>) -> Option<Self> {
243 let data = s.tail()?;
244
245 let number_of_classes = s.read::<u32>()?;
246 let lookup_table_offset = s.read::<Offset32>()?.to_usize();
250 let state_array_offset = s.read::<Offset32>()?.to_usize();
251 let entry_table_offset = s.read::<Offset32>()?.to_usize();
252
253 Some(ExtendedStateTable {
254 number_of_classes,
255 lookup: Lookup::parse(number_of_glyphs, data.get(lookup_table_offset..)?)?,
256 state_array: data.get(state_array_offset..)?,
260 entry_table: data.get(entry_table_offset..)?,
261 entry_type: core::marker::PhantomData,
262 })
263 }
264
265 #[inline]
267 pub fn class(&self, glyph_id: GlyphId) -> Option<u16> {
268 if glyph_id.0 == 0xFFFF {
269 return Some(u16::from(class::DELETED_GLYPH));
270 }
271
272 self.lookup.value(glyph_id)
273 }
274
275 #[inline]
277 pub fn entry(&self, state: u16, mut class: u16) -> Option<GenericStateEntry<T>> {
278 if u32::from(class) >= self.number_of_classes {
279 class = u16::from(class::OUT_OF_BOUNDS);
280 }
281
282 let state_idx =
283 usize::from(state) * usize::num_from(self.number_of_classes) + usize::from(class);
284
285 let entry_idx: u16 = Stream::read_at(self.state_array, state_idx * u16::SIZE)?;
286 Stream::read_at(self.entry_table, usize::from(entry_idx) * GenericStateEntry::<T>::SIZE)
287 }
288}
289
290impl<T> core::fmt::Debug for ExtendedStateTable<'_, T> {
291 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
292 write!(f, "ExtendedStateTable {{ ... }}")
293 }
294}
295
296
297#[derive(Clone)]
303pub struct Lookup<'a> {
304 data: LookupInner<'a>,
305}
306
307impl<'a> Lookup<'a> {
308 #[inline]
312 pub fn parse(number_of_glyphs: NonZeroU16, data: &'a [u8]) -> Option<Self> {
313 LookupInner::parse(number_of_glyphs, data).map(|data| Self { data })
314 }
315
316 #[inline]
318 pub fn value(&self, glyph_id: GlyphId) -> Option<u16> {
319 self.data.value(glyph_id)
320 }
321}
322
323impl core::fmt::Debug for Lookup<'_> {
324 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
325 write!(f, "Lookup {{ ... }}")
326 }
327}
328
329
330#[derive(Clone)]
331enum LookupInner<'a> {
332 Format1(LazyArray16<'a, u16>),
333 Format2(BinarySearchTable<'a, LookupSegment>),
334 Format4(BinarySearchTable<'a, LookupSegment>, &'a [u8]),
335 Format6(BinarySearchTable<'a, LookupSingle>),
336 Format8 {
337 first_glyph: u16,
338 values: LazyArray16<'a, u16>
339 },
340 Format10 {
341 value_size: u16,
342 first_glyph: u16,
343 glyph_count: u16,
344 data: &'a [u8],
345 },
346}
347
348impl<'a> LookupInner<'a> {
349 fn parse(number_of_glyphs: NonZeroU16, data: &'a [u8]) -> Option<Self> {
350 let mut s = Stream::new(data);
351 let format = s.read::<u16>()?;
352 match format {
353 0 => {
354 let values = s.read_array16::<u16>(number_of_glyphs.get())?;
355 Some(Self::Format1(values))
356 }
357 2 => {
358 let bsearch = BinarySearchTable::<LookupSegment>::parse(s.tail()?)?;
359 Some(Self::Format2(bsearch))
360 }
361 4 => {
362 let bsearch = BinarySearchTable::<LookupSegment>::parse(s.tail()?)?;
363 Some(Self::Format4(bsearch, data))
364 }
365 6 => {
366 let bsearch = BinarySearchTable::<LookupSingle>::parse(s.tail()?)?;
367 Some(Self::Format6(bsearch))
368 }
369 8 => {
370 let first_glyph = s.read::<u16>()?;
371 let glyph_count = s.read::<u16>()?;
372 let values = s.read_array16::<u16>(glyph_count)?;
373 Some(Self::Format8 { first_glyph, values })
374 }
375 10 => {
376 let value_size = s.read::<u16>()?;
377 let first_glyph = s.read::<u16>()?;
378 let glyph_count = s.read::<u16>()?;
379 Some(Self::Format10 { value_size, first_glyph, glyph_count, data: s.tail()? })
380 }
381 _ => {
382 None
383 }
384 }
385 }
386
387 fn value(&self, glyph_id: GlyphId) -> Option<u16> {
388 match self {
389 Self::Format1(values) => {
390 values.get(glyph_id.0)
391 }
392 Self::Format2(ref bsearch) => {
393 bsearch.get(glyph_id).map(|v| v.value)
394 }
395 Self::Format4(ref bsearch, data) => {
396 let segment = bsearch.get(glyph_id)?;
399 let index = glyph_id.0.checked_sub(segment.first_glyph)?;
400 let offset = usize::from(segment.value) + u16::SIZE * usize::from(index);
401 Stream::read_at::<u16>(data, offset)
402 }
403 Self::Format6(ref bsearch) => {
404 bsearch.get(glyph_id).map(|v| v.value)
405 }
406 Self::Format8 { first_glyph, values } => {
407 let idx = glyph_id.0.checked_sub(*first_glyph)?;
408 values.get(idx)
409 }
410 Self::Format10 { value_size, first_glyph, glyph_count, data } => {
411 let idx = glyph_id.0.checked_sub(*first_glyph)?;
412 let mut s = Stream::new(data);
413 match value_size {
414 1 => s.read_array16::<u8>(*glyph_count)?.get(idx).map(u16::from),
415 2 => s.read_array16::<u16>(*glyph_count)?.get(idx),
416 4 => s.read_array16::<u32>(*glyph_count)?.get(idx).map(|n| n as u16),
418 _ => None, }
420 }
421 }
422 }
423}
424
425#[derive(Clone)]
428struct BinarySearchTable<'a, T: BinarySearchValue> {
429 values: LazyArray16<'a, T>,
430 len: NonZeroU16, }
432
433impl<'a, T: BinarySearchValue + core::fmt::Debug> BinarySearchTable<'a, T> {
434 #[inline(never)]
435 fn parse(data: &'a [u8]) -> Option<Self> {
436 let mut s = Stream::new(data);
437 let segment_size = s.read::<u16>()?;
438 let number_of_segments = s.read::<u16>()?;
439 s.advance(6); if usize::from(segment_size) != T::SIZE {
442 return None;
443 }
444
445 if number_of_segments == 0 {
446 return None;
447 }
448
449 let values = s.read_array16::<T>(number_of_segments)?;
450
451 let mut len = number_of_segments;
454 if values.last()?.is_termination() {
455 len = len.checked_sub(1)?;
456 }
457
458 Some(BinarySearchTable {
459 len: NonZeroU16::new(len)?,
460 values,
461 })
462 }
463
464 fn get(&self, key: GlyphId) -> Option<T> {
465 let mut min = 0;
466 let mut max = (self.len.get() as isize) - 1;
467 while min <= max {
468 let mid = (min + max) / 2;
469 let v = self.values.get(mid as u16)?;
470 match v.contains(key) {
471 core::cmp::Ordering::Less => max = mid - 1,
472 core::cmp::Ordering::Greater => min = mid + 1,
473 core::cmp::Ordering::Equal => return Some(v),
474 }
475 }
476
477 None
478 }
479}
480
481
482trait BinarySearchValue: FromData {
483 fn is_termination(&self) -> bool;
484 fn contains(&self, glyph_id: GlyphId) -> core::cmp::Ordering;
485}
486
487
488#[derive(Clone, Copy, Debug)]
489struct LookupSegment {
490 last_glyph: u16,
491 first_glyph: u16,
492 value: u16,
493}
494
495impl FromData for LookupSegment {
496 const SIZE: usize = 6;
497
498 #[inline]
499 fn parse(data: &[u8]) -> Option<Self> {
500 let mut s = Stream::new(data);
501 Some(LookupSegment {
502 last_glyph: s.read::<u16>()?,
503 first_glyph: s.read::<u16>()?,
504 value: s.read::<u16>()?,
505 })
506 }
507}
508
509impl BinarySearchValue for LookupSegment {
510 #[inline]
511 fn is_termination(&self) -> bool {
512 self.last_glyph == 0xFFFF && self.first_glyph == 0xFFFF
513 }
514
515 #[inline]
516 fn contains(&self, id: GlyphId) -> core::cmp::Ordering {
517 if id.0 < self.first_glyph {
518 core::cmp::Ordering::Less
519 } else if id.0 <= self.last_glyph {
520 core::cmp::Ordering::Equal
521 } else {
522 core::cmp::Ordering::Greater
523 }
524 }
525}
526
527
528#[derive(Clone, Copy, Debug)]
529struct LookupSingle {
530 glyph: u16,
531 value: u16,
532}
533
534impl FromData for LookupSingle {
535 const SIZE: usize = 4;
536
537 #[inline]
538 fn parse(data: &[u8]) -> Option<Self> {
539 let mut s = Stream::new(data);
540 Some(LookupSingle {
541 glyph: s.read::<u16>()?,
542 value: s.read::<u16>()?,
543 })
544 }
545}
546
547impl BinarySearchValue for LookupSingle {
548 #[inline]
549 fn is_termination(&self) -> bool {
550 self.glyph == 0xFFFF
551 }
552
553 #[inline]
554 fn contains(&self, id: GlyphId) -> core::cmp::Ordering {
555 id.0.cmp(&self.glyph)
556 }
557}