1use crate::parser::{read_u16, read_u32};
29use crate::Error;
30
31#[inline]
40fn read_tag(bytes: &[u8], off: usize) -> Result<[u8; 4], Error> {
41 if bytes.len() < off + 4 {
42 return Err(Error::UnexpectedEof);
43 }
44 Ok([bytes[off], bytes[off + 1], bytes[off + 2], bytes[off + 3]])
45}
46
47#[derive(Debug, Clone, Copy)]
62pub struct ScriptList<'a> {
63 bytes: &'a [u8],
64 count: u16,
65}
66
67impl<'a> ScriptList<'a> {
68 pub fn parse(bytes: &'a [u8]) -> Result<Self, Error> {
70 let count = read_u16(bytes, 0)?;
71 let need = 2usize
72 .checked_add(
73 (count as usize)
74 .checked_mul(6)
75 .ok_or(Error::BadStructure("ScriptList scriptCount overflow"))?,
76 )
77 .ok_or(Error::BadStructure("ScriptList length overflow"))?;
78 if bytes.len() < need {
79 return Err(Error::UnexpectedEof);
80 }
81 Ok(Self { bytes, count })
82 }
83
84 pub fn count(&self) -> u16 {
86 self.count
87 }
88
89 pub fn is_empty(&self) -> bool {
91 self.count == 0
92 }
93
94 pub fn tag(&self, i: u16) -> Option<[u8; 4]> {
96 if i >= self.count {
97 return None;
98 }
99 let off = 2 + (i as usize) * 6;
100 read_tag(self.bytes, off).ok()
101 }
102
103 pub fn script(&self, i: u16) -> Option<Result<Script<'a>, Error>> {
108 if i >= self.count {
109 return None;
110 }
111 let off = 2 + (i as usize) * 6;
112 let script_off = read_u16(self.bytes, off + 4).ok()? as usize;
113 Some(self.script_at(script_off))
114 }
115
116 fn script_at(&self, off: usize) -> Result<Script<'a>, Error> {
117 if off == 0 || off >= self.bytes.len() {
118 return Err(Error::BadStructure("ScriptList: scriptOffset out of range"));
119 }
120 Script::parse(&self.bytes[off..])
121 }
122
123 pub fn find(&self, tag: &[u8; 4]) -> Option<Result<Script<'a>, Error>> {
125 let mut lo = 0i32;
127 let mut hi = self.count as i32 - 1;
128 while lo <= hi {
129 let mid = (lo + hi) / 2;
130 let mid_tag = self.tag(mid as u16)?;
131 match mid_tag.cmp(tag) {
132 std::cmp::Ordering::Equal => {
133 return self.script(mid as u16);
134 }
135 std::cmp::Ordering::Less => lo = mid + 1,
136 std::cmp::Ordering::Greater => hi = mid - 1,
137 }
138 }
139 None
140 }
141
142 pub fn iter(&self) -> ScriptListIter<'a> {
144 ScriptListIter {
145 list: *self,
146 next: 0,
147 }
148 }
149}
150
151#[derive(Debug, Clone)]
153pub struct ScriptListIter<'a> {
154 list: ScriptList<'a>,
155 next: u16,
156}
157
158impl<'a> Iterator for ScriptListIter<'a> {
159 type Item = ([u8; 4], Result<Script<'a>, Error>);
160 fn next(&mut self) -> Option<Self::Item> {
161 if self.next >= self.list.count {
162 return None;
163 }
164 let i = self.next;
165 self.next += 1;
166 let tag = self.list.tag(i)?;
167 let script = self.list.script(i)?;
168 Some((tag, script))
169 }
170}
171
172#[derive(Debug, Clone, Copy)]
185pub struct Script<'a> {
186 bytes: &'a [u8],
187 default_off: u16,
188 count: u16,
189}
190
191impl<'a> Script<'a> {
192 pub fn parse(bytes: &'a [u8]) -> Result<Self, Error> {
193 let default_off = read_u16(bytes, 0)?;
194 let count = read_u16(bytes, 2)?;
195 let need = 4usize
196 .checked_add(
197 (count as usize)
198 .checked_mul(6)
199 .ok_or(Error::BadStructure("Script langSysCount overflow"))?,
200 )
201 .ok_or(Error::BadStructure("Script length overflow"))?;
202 if bytes.len() < need {
203 return Err(Error::UnexpectedEof);
204 }
205 Ok(Self {
206 bytes,
207 default_off,
208 count,
209 })
210 }
211
212 pub fn has_default_lang_sys(&self) -> bool {
214 self.default_off != 0
215 }
216
217 pub fn lang_sys_count(&self) -> u16 {
219 self.count
220 }
221
222 pub fn default_lang_sys(&self) -> Option<Result<LangSys<'a>, Error>> {
224 if self.default_off == 0 {
225 return None;
226 }
227 let off = self.default_off as usize;
228 if off >= self.bytes.len() {
229 return Some(Err(Error::BadStructure(
230 "Script: defaultLangSysOffset out of range",
231 )));
232 }
233 Some(LangSys::parse(&self.bytes[off..]))
234 }
235
236 pub fn lang_sys_tag(&self, i: u16) -> Option<[u8; 4]> {
238 if i >= self.count {
239 return None;
240 }
241 let off = 4 + (i as usize) * 6;
242 read_tag(self.bytes, off).ok()
243 }
244
245 pub fn lang_sys(&self, i: u16) -> Option<Result<LangSys<'a>, Error>> {
247 if i >= self.count {
248 return None;
249 }
250 let off = 4 + (i as usize) * 6;
251 let lang_off = read_u16(self.bytes, off + 4).ok()? as usize;
252 if lang_off == 0 || lang_off >= self.bytes.len() {
253 return Some(Err(Error::BadStructure(
254 "Script: langSysOffset out of range",
255 )));
256 }
257 Some(LangSys::parse(&self.bytes[lang_off..]))
258 }
259
260 pub fn find_lang_sys(&self, tag: &[u8; 4]) -> Option<Result<LangSys<'a>, Error>> {
262 let mut lo = 0i32;
263 let mut hi = self.count as i32 - 1;
264 while lo <= hi {
265 let mid = (lo + hi) / 2;
266 let mid_tag = self.lang_sys_tag(mid as u16)?;
267 match mid_tag.cmp(tag) {
268 std::cmp::Ordering::Equal => return self.lang_sys(mid as u16),
269 std::cmp::Ordering::Less => lo = mid + 1,
270 std::cmp::Ordering::Greater => hi = mid - 1,
271 }
272 }
273 None
274 }
275}
276
277pub const NO_REQUIRED_FEATURE: u16 = 0xFFFF;
283
284#[derive(Debug, Clone, Copy)]
294pub struct LangSys<'a> {
295 bytes: &'a [u8],
296 required: u16,
297 count: u16,
298}
299
300impl<'a> LangSys<'a> {
301 pub fn parse(bytes: &'a [u8]) -> Result<Self, Error> {
302 let _ = read_u16(bytes, 0)?;
304 let required = read_u16(bytes, 2)?;
305 let count = read_u16(bytes, 4)?;
306 let need = 6usize
307 .checked_add(
308 (count as usize)
309 .checked_mul(2)
310 .ok_or(Error::BadStructure("LangSys featureIndexCount overflow"))?,
311 )
312 .ok_or(Error::BadStructure("LangSys length overflow"))?;
313 if bytes.len() < need {
314 return Err(Error::UnexpectedEof);
315 }
316 Ok(Self {
317 bytes,
318 required,
319 count,
320 })
321 }
322
323 pub fn required_feature_index(&self) -> Option<u16> {
325 if self.required == NO_REQUIRED_FEATURE {
326 None
327 } else {
328 Some(self.required)
329 }
330 }
331
332 pub fn feature_count(&self) -> u16 {
334 self.count
335 }
336
337 pub fn feature_index(&self, i: u16) -> Option<u16> {
339 if i >= self.count {
340 return None;
341 }
342 let off = 6 + (i as usize) * 2;
343 read_u16(self.bytes, off).ok()
344 }
345
346 pub fn feature_indices(&self) -> impl Iterator<Item = u16> + '_ {
348 (0..self.count).filter_map(move |i| self.feature_index(i))
349 }
350}
351
352#[derive(Debug, Clone, Copy)]
369pub struct FeatureList<'a> {
370 bytes: &'a [u8],
371 count: u16,
372}
373
374impl<'a> FeatureList<'a> {
375 pub fn parse(bytes: &'a [u8]) -> Result<Self, Error> {
376 let count = read_u16(bytes, 0)?;
377 let need = 2usize
378 .checked_add(
379 (count as usize)
380 .checked_mul(6)
381 .ok_or(Error::BadStructure("FeatureList featureCount overflow"))?,
382 )
383 .ok_or(Error::BadStructure("FeatureList length overflow"))?;
384 if bytes.len() < need {
385 return Err(Error::UnexpectedEof);
386 }
387 Ok(Self { bytes, count })
388 }
389
390 pub fn count(&self) -> u16 {
392 self.count
393 }
394
395 pub fn is_empty(&self) -> bool {
397 self.count == 0
398 }
399
400 pub fn tag(&self, i: u16) -> Option<[u8; 4]> {
402 if i >= self.count {
403 return None;
404 }
405 let off = 2 + (i as usize) * 6;
406 read_tag(self.bytes, off).ok()
407 }
408
409 pub fn feature(&self, i: u16) -> Option<Result<Feature<'a>, Error>> {
411 if i >= self.count {
412 return None;
413 }
414 let off = 2 + (i as usize) * 6;
415 let feat_off = read_u16(self.bytes, off + 4).ok()? as usize;
416 if feat_off == 0 || feat_off >= self.bytes.len() {
417 return Some(Err(Error::BadStructure(
418 "FeatureList: featureOffset out of range",
419 )));
420 }
421 Some(Feature::parse(&self.bytes[feat_off..]))
422 }
423
424 pub fn iter(&self) -> FeatureListIter<'a> {
426 FeatureListIter {
427 list: *self,
428 next: 0,
429 }
430 }
431}
432
433#[derive(Debug, Clone)]
435pub struct FeatureListIter<'a> {
436 list: FeatureList<'a>,
437 next: u16,
438}
439
440impl<'a> Iterator for FeatureListIter<'a> {
441 type Item = ([u8; 4], Result<Feature<'a>, Error>);
442 fn next(&mut self) -> Option<Self::Item> {
443 if self.next >= self.list.count {
444 return None;
445 }
446 let i = self.next;
447 self.next += 1;
448 let tag = self.list.tag(i)?;
449 let feat = self.list.feature(i)?;
450 Some((tag, feat))
451 }
452}
453
454#[derive(Debug, Clone, Copy)]
467pub struct Feature<'a> {
468 bytes: &'a [u8],
469 params_off: u16,
470 count: u16,
471}
472
473impl<'a> Feature<'a> {
474 pub fn parse(bytes: &'a [u8]) -> Result<Self, Error> {
475 let params_off = read_u16(bytes, 0)?;
476 let count = read_u16(bytes, 2)?;
477 let need = 4usize
478 .checked_add(
479 (count as usize)
480 .checked_mul(2)
481 .ok_or(Error::BadStructure("Feature lookupIndexCount overflow"))?,
482 )
483 .ok_or(Error::BadStructure("Feature length overflow"))?;
484 if bytes.len() < need {
485 return Err(Error::UnexpectedEof);
486 }
487 Ok(Self {
488 bytes,
489 params_off,
490 count,
491 })
492 }
493
494 pub fn feature_params_offset(&self) -> u16 {
499 self.params_off
500 }
501
502 pub fn lookup_count(&self) -> u16 {
504 self.count
505 }
506
507 pub fn lookup_index(&self, i: u16) -> Option<u16> {
509 if i >= self.count {
510 return None;
511 }
512 let off = 4 + (i as usize) * 2;
513 read_u16(self.bytes, off).ok()
514 }
515
516 pub fn lookup_indices(&self) -> impl Iterator<Item = u16> + '_ {
518 (0..self.count).filter_map(move |i| self.lookup_index(i))
519 }
520}
521
522#[derive(Debug, Clone, Copy)]
534pub struct LookupList<'a> {
535 bytes: &'a [u8],
536 count: u16,
537}
538
539impl<'a> LookupList<'a> {
540 pub fn parse(bytes: &'a [u8]) -> Result<Self, Error> {
541 let count = read_u16(bytes, 0)?;
542 let need = 2usize
543 .checked_add(
544 (count as usize)
545 .checked_mul(2)
546 .ok_or(Error::BadStructure("LookupList lookupCount overflow"))?,
547 )
548 .ok_or(Error::BadStructure("LookupList length overflow"))?;
549 if bytes.len() < need {
550 return Err(Error::UnexpectedEof);
551 }
552 Ok(Self { bytes, count })
553 }
554
555 pub fn count(&self) -> u16 {
557 self.count
558 }
559
560 pub fn is_empty(&self) -> bool {
562 self.count == 0
563 }
564
565 pub fn lookup(&self, i: u16) -> Option<Result<Lookup<'a>, Error>> {
567 if i >= self.count {
568 return None;
569 }
570 let off = 2 + (i as usize) * 2;
571 let lkup_off = read_u16(self.bytes, off).ok()? as usize;
572 if lkup_off == 0 || lkup_off >= self.bytes.len() {
573 return Some(Err(Error::BadStructure(
574 "LookupList: lookupOffset out of range",
575 )));
576 }
577 Some(Lookup::parse(&self.bytes[lkup_off..]))
578 }
579
580 pub fn iter(&self) -> LookupListIter<'a> {
582 LookupListIter {
583 list: *self,
584 next: 0,
585 }
586 }
587}
588
589#[derive(Debug, Clone)]
591pub struct LookupListIter<'a> {
592 list: LookupList<'a>,
593 next: u16,
594}
595
596impl<'a> Iterator for LookupListIter<'a> {
597 type Item = Result<Lookup<'a>, Error>;
598 fn next(&mut self) -> Option<Self::Item> {
599 if self.next >= self.list.count {
600 return None;
601 }
602 let i = self.next;
603 self.next += 1;
604 self.list.lookup(i)
605 }
606}
607
608#[derive(Debug, Clone, Copy, PartialEq, Eq)]
611pub struct LookupFlag(pub u16);
612
613impl LookupFlag {
614 pub const RIGHT_TO_LEFT: u16 = 0x0001;
616 pub const IGNORE_BASE_GLYPHS: u16 = 0x0002;
618 pub const IGNORE_LIGATURES: u16 = 0x0004;
620 pub const IGNORE_MARKS: u16 = 0x0008;
622 pub const USE_MARK_FILTERING_SET: u16 = 0x0010;
624 pub const MARK_ATTACHMENT_CLASS_MASK: u16 = 0xFF00;
626
627 pub fn bits(self) -> u16 {
629 self.0
630 }
631
632 pub fn right_to_left(self) -> bool {
634 self.0 & Self::RIGHT_TO_LEFT != 0
635 }
636
637 pub fn ignore_base_glyphs(self) -> bool {
639 self.0 & Self::IGNORE_BASE_GLYPHS != 0
640 }
641
642 pub fn ignore_ligatures(self) -> bool {
644 self.0 & Self::IGNORE_LIGATURES != 0
645 }
646
647 pub fn ignore_marks(self) -> bool {
649 self.0 & Self::IGNORE_MARKS != 0
650 }
651
652 pub fn use_mark_filtering_set(self) -> bool {
656 self.0 & Self::USE_MARK_FILTERING_SET != 0
657 }
658
659 pub fn mark_attachment_type(self) -> u8 {
662 ((self.0 & Self::MARK_ATTACHMENT_CLASS_MASK) >> 8) as u8
663 }
664}
665
666#[derive(Debug, Clone, Copy)]
680pub struct Lookup<'a> {
681 bytes: &'a [u8],
682 lookup_type: u16,
683 flag: LookupFlag,
684 sub_count: u16,
685 mark_filtering_set: u16,
689}
690
691impl<'a> Lookup<'a> {
692 pub fn parse(bytes: &'a [u8]) -> Result<Self, Error> {
693 let lookup_type = read_u16(bytes, 0)?;
694 let flag_bits = read_u16(bytes, 2)?;
695 let flag = LookupFlag(flag_bits);
696 let sub_count = read_u16(bytes, 4)?;
697 let sub_array = 6usize
698 .checked_add(
699 (sub_count as usize)
700 .checked_mul(2)
701 .ok_or(Error::BadStructure("Lookup subTableCount overflow"))?,
702 )
703 .ok_or(Error::BadStructure("Lookup length overflow"))?;
704 let need = if flag.use_mark_filtering_set() {
705 sub_array
706 .checked_add(2)
707 .ok_or(Error::BadStructure("Lookup mark-filtering overflow"))?
708 } else {
709 sub_array
710 };
711 if bytes.len() < need {
712 return Err(Error::UnexpectedEof);
713 }
714 let mark_filtering_set = if flag.use_mark_filtering_set() {
715 read_u16(bytes, sub_array)?
716 } else {
717 0
718 };
719 Ok(Self {
720 bytes,
721 lookup_type,
722 flag,
723 sub_count,
724 mark_filtering_set,
725 })
726 }
727
728 pub fn lookup_type(&self) -> u16 {
731 self.lookup_type
732 }
733
734 pub fn flag(&self) -> LookupFlag {
736 self.flag
737 }
738
739 pub fn subtable_count(&self) -> u16 {
741 self.sub_count
742 }
743
744 pub fn subtable_bytes(&self, i: u16) -> Option<&'a [u8]> {
749 if i >= self.sub_count {
750 return None;
751 }
752 let off_off = 6 + (i as usize) * 2;
753 let sub_off = read_u16(self.bytes, off_off).ok()? as usize;
754 if sub_off == 0 || sub_off >= self.bytes.len() {
755 return None;
756 }
757 Some(&self.bytes[sub_off..])
758 }
759
760 pub fn mark_filtering_set(&self) -> Option<u16> {
763 if self.flag.use_mark_filtering_set() {
764 Some(self.mark_filtering_set)
765 } else {
766 None
767 }
768 }
769}
770
771#[derive(Debug, Clone, Copy)]
779pub(crate) struct LayoutHeader {
780 pub(crate) major: u16,
781 pub(crate) minor: u16,
782 pub(crate) script_list_off: u16,
783 pub(crate) feature_list_off: u16,
784 pub(crate) lookup_list_off: u16,
785 pub(crate) feature_variations_off: u32,
788}
789
790impl LayoutHeader {
791 pub(crate) fn parse(bytes: &[u8]) -> Result<Self, Error> {
795 if bytes.len() < 10 {
796 return Err(Error::UnexpectedEof);
797 }
798 let major = read_u16(bytes, 0)?;
799 let minor = read_u16(bytes, 2)?;
800 if major != 1 || minor > 1 {
801 return Err(Error::BadStructure(
802 "GSUB/GPOS: only version 1.0 / 1.1 are defined",
803 ));
804 }
805 let script_list_off = read_u16(bytes, 4)?;
806 let feature_list_off = read_u16(bytes, 6)?;
807 let lookup_list_off = read_u16(bytes, 8)?;
808 let feature_variations_off = if minor == 1 {
809 if bytes.len() < 14 {
810 return Err(Error::UnexpectedEof);
811 }
812 read_u32(bytes, 10)?
813 } else {
814 0
815 };
816 Ok(Self {
817 major,
818 minor,
819 script_list_off,
820 feature_list_off,
821 lookup_list_off,
822 feature_variations_off,
823 })
824 }
825}
826
827#[cfg(test)]
832mod tests {
833 use super::*;
834
835 fn be(u: u16) -> [u8; 2] {
836 u.to_be_bytes()
837 }
838 fn be32(u: u32) -> [u8; 4] {
839 u.to_be_bytes()
840 }
841
842 #[test]
843 fn header_v10_round_trip() {
844 let mut bytes = Vec::new();
845 bytes.extend_from_slice(&be(1));
846 bytes.extend_from_slice(&be(0));
847 bytes.extend_from_slice(&be(10));
848 bytes.extend_from_slice(&be(20));
849 bytes.extend_from_slice(&be(30));
850 let h = LayoutHeader::parse(&bytes).unwrap();
851 assert_eq!(h.major, 1);
852 assert_eq!(h.minor, 0);
853 assert_eq!(h.script_list_off, 10);
854 assert_eq!(h.feature_list_off, 20);
855 assert_eq!(h.lookup_list_off, 30);
856 assert_eq!(h.feature_variations_off, 0);
857 }
858
859 #[test]
860 fn header_v11_round_trip() {
861 let mut bytes = Vec::new();
862 bytes.extend_from_slice(&be(1));
863 bytes.extend_from_slice(&be(1));
864 bytes.extend_from_slice(&be(14));
865 bytes.extend_from_slice(&be(24));
866 bytes.extend_from_slice(&be(34));
867 bytes.extend_from_slice(&be32(99_999));
868 let h = LayoutHeader::parse(&bytes).unwrap();
869 assert_eq!(h.minor, 1);
870 assert_eq!(h.feature_variations_off, 99_999);
871 }
872
873 #[test]
874 fn header_rejects_unknown_versions() {
875 let bytes_v2 = {
876 let mut b = vec![0u8; 14];
877 b[0..2].copy_from_slice(&be(2));
878 b
879 };
880 assert!(matches!(
881 LayoutHeader::parse(&bytes_v2),
882 Err(Error::BadStructure(_))
883 ));
884 let bytes_v12 = {
885 let mut b = vec![0u8; 14];
886 b[0..2].copy_from_slice(&be(1));
887 b[2..4].copy_from_slice(&be(2));
888 b
889 };
890 assert!(matches!(
891 LayoutHeader::parse(&bytes_v12),
892 Err(Error::BadStructure(_))
893 ));
894 }
895
896 #[test]
900 fn synthetic_round_trip() {
901 let mut lang_sys = Vec::new();
904 lang_sys.extend_from_slice(&be(0));
905 lang_sys.extend_from_slice(&be(NO_REQUIRED_FEATURE));
906 lang_sys.extend_from_slice(&be(2));
907 lang_sys.extend_from_slice(&be(0));
908 lang_sys.extend_from_slice(&be(1));
909 let ls = LangSys::parse(&lang_sys).unwrap();
910 assert!(ls.required_feature_index().is_none());
911 assert_eq!(ls.feature_count(), 2);
912 assert_eq!(ls.feature_index(0), Some(0));
913 assert_eq!(ls.feature_index(1), Some(1));
914 assert_eq!(ls.feature_index(2), None);
915 let v: Vec<_> = ls.feature_indices().collect();
916 assert_eq!(v, vec![0, 1]);
917
918 let mut feature = Vec::new();
920 feature.extend_from_slice(&be(0));
921 feature.extend_from_slice(&be(1));
922 feature.extend_from_slice(&be(0));
923 let f = Feature::parse(&feature).unwrap();
924 assert_eq!(f.feature_params_offset(), 0);
925 assert_eq!(f.lookup_count(), 1);
926 assert_eq!(f.lookup_index(0), Some(0));
927
928 let mut lookup = Vec::new();
930 lookup.extend_from_slice(&be(1));
931 lookup.extend_from_slice(&be(0));
932 lookup.extend_from_slice(&be(0));
933 let l = Lookup::parse(&lookup).unwrap();
934 assert_eq!(l.lookup_type(), 1);
935 assert_eq!(l.subtable_count(), 0);
936 assert!(l.mark_filtering_set().is_none());
937 assert!(!l.flag().right_to_left());
938 assert!(!l.flag().ignore_marks());
939 }
940
941 #[test]
942 fn lookup_with_mark_filtering_set() {
943 let mut lookup = Vec::new();
944 lookup.extend_from_slice(&be(4));
945 lookup.extend_from_slice(&be(LookupFlag::USE_MARK_FILTERING_SET));
946 lookup.extend_from_slice(&be(0));
947 lookup.extend_from_slice(&be(7));
948 let l = Lookup::parse(&lookup).unwrap();
949 assert!(l.flag().use_mark_filtering_set());
950 assert_eq!(l.mark_filtering_set(), Some(7));
951 }
952
953 #[test]
954 fn lookup_flag_helpers() {
955 let f = LookupFlag(0x0A0E);
956 assert!(!f.right_to_left());
957 assert!(f.ignore_base_glyphs());
958 assert!(f.ignore_ligatures());
959 assert!(f.ignore_marks());
960 assert!(!f.use_mark_filtering_set());
961 assert_eq!(f.mark_attachment_type(), 0x0A);
962 }
963
964 #[test]
965 fn truncation_surfaces_unexpected_eof() {
966 let mut bytes = Vec::new();
968 bytes.extend_from_slice(&be(3));
969 bytes.extend_from_slice(b"DFLT");
970 bytes.extend_from_slice(&be(0));
971 bytes.extend_from_slice(b"latn");
972 bytes.extend_from_slice(&be(0));
973 assert!(matches!(
975 ScriptList::parse(&bytes),
976 Err(Error::UnexpectedEof)
977 ));
978
979 let mut lookup = Vec::new();
981 lookup.extend_from_slice(&be(4));
982 lookup.extend_from_slice(&be(LookupFlag::USE_MARK_FILTERING_SET));
983 lookup.extend_from_slice(&be(0));
984 assert!(matches!(Lookup::parse(&lookup), Err(Error::UnexpectedEof)));
985 }
986}