1pub mod ascii;
42pub mod capture;
43pub mod event;
44pub mod iter;
45pub mod signature;
46
47use smallvec::SmallVec;
48
49use ascii::AsciiControl;
50use event::{CSI, DCS, Esc, EscInvalid, SS2, SS3, VTEvent, VTIntermediate};
51
52const ESC: u8 = AsciiControl::Esc as _;
53const BEL: u8 = AsciiControl::Bel as _;
54const DEL: u8 = AsciiControl::Del as _;
55const CAN: u8 = AsciiControl::Can as _;
56const SUB: u8 = AsciiControl::Sub as _;
57const CSI: u8 = b'[';
58const OSC: u8 = b']';
59const SS2: u8 = b'N';
60const SS3: u8 = b'O';
61const DCS: u8 = b'P';
62const APC: u8 = b'_';
63const PM: u8 = b'^';
64const SOS: u8 = b'X';
65const ST_FINAL: u8 = b'\\';
66
67use crate::event::{Param, ParamBuf, Params};
68
69#[allow(private_bounds)]
71pub trait VTEventCallback: VTEventCallbackMaybeAbortable<()> {
72 fn event(&mut self, event: VTEvent<'_>) -> ();
73}
74
75impl<T: FnMut(VTEvent<'_>)> VTEventCallback for T {
76 #[inline(always)]
77 fn event(&mut self, event: VTEvent<'_>) -> () {
78 self(event)
79 }
80}
81
82impl<T: VTEventCallback> VTEventCallbackMaybeAbortable<()> for T {
83 #[inline(always)]
84 fn event(&mut self, event: VTEvent<'_>) -> () {
85 VTEventCallback::event(self, event)
86 }
87}
88
89#[allow(private_bounds)]
92pub trait VTEventCallbackAbortable: VTEventCallbackMaybeAbortable<bool> {
93 fn event(&mut self, event: VTEvent<'_>) -> bool;
94}
95
96impl<T: VTEventCallbackAbortable> VTEventCallbackMaybeAbortable<bool> for T {
97 #[inline(always)]
98 fn event(&mut self, event: VTEvent<'_>) -> bool {
99 VTEventCallbackAbortable::event(self, event)
100 }
101}
102
103impl<T: FnMut(VTEvent<'_>) -> bool> VTEventCallbackAbortable for T {
104 #[inline(always)]
105 fn event(&mut self, event: VTEvent<'_>) -> bool {
106 self(event)
107 }
108}
109
110trait VTEventCallbackMaybeAbortable<R: MaybeAbortable> {
111 fn event<'e>(&mut self, event: VTEvent<'e>) -> R;
112}
113
114enum VTAction<'a> {
116 None,
119 Event(VTEvent<'a>),
122 End(VTEnd),
124 Buffer(VTEmit),
127 Hold(VTEmit),
130 Cancel(VTEmit),
132}
133
134#[derive(Debug, Copy, Clone, PartialEq, Eq)]
135enum VTEmit {
136 Ground,
138 Dcs,
140 Osc,
142}
143
144#[derive(Debug, Copy, Clone, PartialEq, Eq)]
145enum VTEnd {
146 Dcs,
148 Osc { used_bel: bool },
150}
151
152#[inline]
153const fn is_c0(b: u8) -> bool {
154 b <= 0x1F && b != b'\r' && b != b'\n' && b != b'\t'
156}
157#[inline]
158fn is_printable(b: u8) -> bool {
159 (0x20..=0x7E).contains(&b)
160}
161#[inline]
162fn is_intermediate(b: u8) -> bool {
163 (0x20..=0x2F).contains(&b)
164}
165#[inline]
166const fn is_final(b: u8) -> bool {
167 b >= 0x40 && b <= 0x7E
168}
169#[inline]
170fn is_digit(b: u8) -> bool {
171 b.is_ascii_digit()
172}
173#[inline]
174fn is_priv(b: u8) -> bool {
175 matches!(b, b'<' | b'=' | b'>' | b'?')
176}
177
178macro_rules! byte_predicate {
179 (|$p:ident| $body:block) => {{
180 let mut out: [bool; 256] = [false; 256];
181 let mut i = 0;
182 while i < 256 {
183 let $p: u8 = i as u8;
184 out[i] = $body;
185 i += 1;
186 }
187 out
188 }};
189}
190
191const ENDS_CSI: [bool; 256] =
192 byte_predicate!(|b| { is_final(b) || b == ESC || b == CAN || b == SUB });
193
194const ENDS_GROUND: [bool; 256] = byte_predicate!(|b| { is_c0(b) || b == DEL });
195
196#[derive(Debug, Copy, Clone, PartialEq, Eq)]
197enum State {
198 Ground,
199 Escape,
200 EscInt,
201 EscSs2,
202 EscSs3,
203 CsiEntry,
204 CsiParam,
205 CsiInt,
206 CsiIgnore,
207 DcsEntry,
208 DcsParam,
209 DcsInt,
210 DcsIgnore,
211 DcsIgnoreEsc,
212 DcsPassthrough,
213 DcsEsc,
214 OscString,
215 OscEsc,
216 SosPmApcString,
217 SpaEsc,
218}
219
220pub const VT_PARSER_INTEREST_NONE: u8 = 0;
222pub const VT_PARSER_INTEREST_CSI: u8 = 1 << 0;
224pub const VT_PARSER_INTEREST_DCS: u8 = 1 << 1;
226pub const VT_PARSER_INTEREST_OSC: u8 = 1 << 2;
228pub const VT_PARSER_INTEREST_ESCAPE_RECOVERY: u8 = 1 << 4;
230pub const VT_PARSER_INTEREST_OTHER: u8 = 1 << 5;
232
233pub const VT_PARSER_INTEREST_ALL: u8 = VT_PARSER_INTEREST_CSI
235 | VT_PARSER_INTEREST_DCS
236 | VT_PARSER_INTEREST_OSC
237 | VT_PARSER_INTEREST_ESCAPE_RECOVERY
238 | VT_PARSER_INTEREST_OTHER;
239
240pub const VT_PARSER_INTEREST_DEFAULT: u8 = VT_PARSER_INTEREST_CSI
242 | VT_PARSER_INTEREST_DCS
243 | VT_PARSER_INTEREST_OSC
244 | VT_PARSER_INTEREST_OTHER;
245
246#[must_use]
247trait MaybeAbortable {
248 fn abort(self) -> bool;
249}
250
251impl MaybeAbortable for bool {
252 #[inline(always)]
253 fn abort(self) -> bool {
254 !self
255 }
256}
257
258impl MaybeAbortable for () {
259 #[inline(always)]
260 fn abort(self) -> bool {
261 false
262 }
263}
264
265pub struct VTPushParser<const INTEREST: u8 = VT_PARSER_INTEREST_DEFAULT> {
270 st: State,
271
272 ints: VTIntermediate,
274 params: Params,
275 cur_param: Param,
276 priv_prefix: Option<u8>,
277 held_byte: Option<u8>,
278}
279
280impl Default for VTPushParser {
281 fn default() -> Self {
282 Self::new()
283 }
284}
285
286impl VTPushParser {
287 pub const fn new() -> Self {
288 VTPushParser::new_with()
289 }
290
291 pub fn decode_buffer<'a>(input: &'a [u8], mut cb: impl for<'b> FnMut(VTEvent<'b>)) {
293 let mut parser = VTPushParser::new();
294 parser.feed_with(input, &mut cb);
295 }
296
297 pub const fn new_with_interest<const INTEREST: u8>() -> VTPushParser<INTEREST> {
298 VTPushParser::new_with()
299 }
300}
301
302macro_rules! invalid {
304 ($self:ident .priv_prefix, $self_:ident .ints, $b:expr) => {
305 if let Some(p) = $self.priv_prefix {
306 if $self.ints.len() == 0 {
307 VTEvent::EscInvalid(EscInvalid::Two(p, $b))
308 } else if $self.ints.len() == 1 {
309 VTEvent::EscInvalid(EscInvalid::Three(p, $self.ints.data[0], $b))
310 } else {
311 VTEvent::EscInvalid(EscInvalid::Four(
312 p,
313 $self.ints.data[0],
314 $self.ints.data[1],
315 $b,
316 ))
317 }
318 } else {
319 if $self.ints.len() == 0 {
320 VTEvent::EscInvalid(EscInvalid::One($b))
321 } else if $self.ints.len() == 1 {
322 VTEvent::EscInvalid(EscInvalid::Two($self.ints.data[0], $b))
323 } else {
324 VTEvent::EscInvalid(EscInvalid::Three(
325 $self.ints.data[0],
326 $self.ints.data[1],
327 $b,
328 ))
329 }
330 }
331 };
332 ($self:ident .priv_prefix, $self_:ident .ints) => {
333 if let Some(p) = $self.priv_prefix {
334 if $self.ints.len() == 0 {
335 VTEvent::EscInvalid(EscInvalid::One(p))
336 } else if $self.ints.len() == 1 {
337 VTEvent::EscInvalid(EscInvalid::Two(p, $self.ints.data[0]))
338 } else {
339 VTEvent::EscInvalid(EscInvalid::Three(p, $self.ints.data[0], $self.ints.data[1]))
340 }
341 } else {
342 if $self.ints.len() == 0 {
343 VTEvent::C0(0x1b)
345 } else if $self.ints.len() == 1 {
346 VTEvent::EscInvalid(EscInvalid::One($self.ints.data[0]))
347 } else {
348 VTEvent::EscInvalid(EscInvalid::Two($self.ints.data[0], $self.ints.data[1]))
349 }
350 }
351 };
352 ($a:expr) => {
353 VTEvent::EscInvalid(EscInvalid::One($a))
354 };
355 ($a:expr, $b:expr) => {
356 VTEvent::EscInvalid(EscInvalid::Two($a, $b))
357 };
358}
359
360impl<const INTEREST: u8> VTPushParser<INTEREST> {
361 const fn new_with() -> Self {
362 Self {
363 st: State::Ground,
364 ints: VTIntermediate::empty(),
365 params: SmallVec::new_const(),
366 cur_param: SmallVec::new_const(),
367 priv_prefix: None,
368 held_byte: None,
369 }
370 }
371
372 #[inline]
416 pub fn feed_with<'this, 'input, F: VTEventCallback>(
417 &'this mut self,
418 input: &'input [u8],
419 mut cb: F,
420 ) {
421 self.feed_with_internal(input, &mut cb);
422 }
423
424 #[inline]
437 pub fn feed_with_abortable<'this, 'input, F: VTEventCallbackAbortable>(
438 &'this mut self,
439 input: &'input [u8],
440 mut cb: F,
441 ) -> usize {
442 self.feed_with_internal(input, &mut cb)
443 }
444
445 #[inline(always)]
446 fn feed_with_internal<'this, 'input, R: MaybeAbortable, F: VTEventCallbackMaybeAbortable<R>>(
447 &'this mut self,
448 input: &'input [u8],
449 cb: &mut F,
450 ) -> usize {
451 if input.is_empty() {
452 return 0;
453 }
454
455 #[derive(Debug)]
456 struct FeedState {
457 buffer_idx: usize,
458 current_emit: Option<VTEmit>,
459 hold: bool,
460 }
461
462 let mut state = FeedState {
463 buffer_idx: 0,
464 current_emit: None,
465 hold: self.held_byte.is_some(),
466 };
467
468 macro_rules! emit {
469 ($state:ident, $i:expr, $cb:expr, $end:expr, $used_bel:expr) => {
470 let hold = std::mem::take(&mut $state.hold);
471 if let Some(emit) = $state.current_emit.take() {
472 let i = $i;
473 let range = $state.buffer_idx..(i - hold as usize);
474 if $end {
475 if match emit {
476 VTEmit::Ground => unreachable!(),
477 VTEmit::Dcs => $cb.event(VTEvent::DcsEnd(&input[range])),
478 VTEmit::Osc => $cb.event(VTEvent::OscEnd {
479 data: &input[range],
480 used_bel: $used_bel,
481 }),
482 }
483 .abort()
484 {
485 return i + 1;
486 }
487 } else if range.len() > 0 {
488 if match emit {
489 VTEmit::Ground => $cb.event(VTEvent::Raw(&input[range])),
490 VTEmit::Dcs => $cb.event(VTEvent::DcsData(&input[range])),
491 VTEmit::Osc => $cb.event(VTEvent::OscData(&input[range])),
492 }
493 .abort()
494 {
495 return i + 1;
496 }
497 }
498 }
499 };
500 }
501
502 let mut held_byte = self.held_byte.take();
503 let mut i = 0;
504
505 while i < input.len() {
506 if self.st == State::Ground {
508 let start = i;
509 loop {
510 if i >= input.len() {
511 cb.event(VTEvent::Raw(&input[start..]));
512 return input.len();
513 }
514 if ENDS_GROUND[input[i] as usize] {
515 break;
516 }
517 i += 1;
518 }
519
520 if start != i && cb.event(VTEvent::Raw(&input[start..i])).abort() {
521 return i;
522 }
523
524 if input[i] == ESC {
525 self.clear_hdr_collectors();
526 self.st = State::Escape;
527 i += 1;
528 continue;
529 }
530 }
531
532 if self.st == State::CsiIgnore {
534 loop {
535 if i >= input.len() {
536 return input.len();
537 }
538 if ENDS_CSI[input[i] as usize] {
539 break;
540 }
541 i += 1;
542 }
543
544 if input[i] == ESC {
545 self.st = State::Escape;
546 } else {
547 self.st = State::Ground;
548 }
549 i += 1;
550 continue;
551 }
552
553 let action = self.push_with(input[i]);
554
555 match action {
556 VTAction::None => {
557 if let Some(emit) = state.current_emit {
558 let range = state.buffer_idx..(i - state.hold as usize);
560 if !range.is_empty()
561 && match emit {
562 VTEmit::Ground => cb.event(VTEvent::Raw(&input[range])),
563 VTEmit::Dcs => cb.event(VTEvent::DcsData(&input[range])),
564 VTEmit::Osc => cb.event(VTEvent::OscData(&input[range])),
565 }
566 .abort()
567 {
568 if state.hold {
569 self.held_byte = Some(0x1b);
570 }
571 return i + 1;
572 }
573 if state.hold {
574 held_byte = Some(0x1b);
575 }
576 state.current_emit = None;
577 }
578 }
579 VTAction::Event(e) => {
580 if cb.event(e).abort() {
581 return i + 1;
582 }
583 }
584 VTAction::End(VTEnd::Dcs) => {
585 held_byte = None;
586 emit!(state, i, cb, true, false);
587 }
588 VTAction::End(VTEnd::Osc { used_bel }) => {
589 held_byte = None;
590 emit!(state, i, cb, true, used_bel);
591 }
592 VTAction::Buffer(emit) | VTAction::Hold(emit) => {
593 if state.current_emit.is_none()
594 && let Some(h) = held_byte.take()
595 && match emit {
596 VTEmit::Ground => cb.event(VTEvent::Raw(&[h])),
597 VTEmit::Dcs => cb.event(VTEvent::DcsData(&[h])),
598 VTEmit::Osc => cb.event(VTEvent::OscData(&[h])),
599 }
600 .abort()
601 {
602 if matches!(action, VTAction::Hold(_)) {
603 self.held_byte = Some(0x1b);
604 return 1;
605 }
606 return 0;
607 }
608
609 debug_assert!(state.current_emit.is_none() || state.current_emit == Some(emit));
610
611 state.hold = matches!(action, VTAction::Hold(_));
612 if state.current_emit.is_none() {
613 state.buffer_idx = i;
614 state.current_emit = Some(emit);
615 }
616 }
617 VTAction::Cancel(emit) => {
618 state.current_emit = None;
619 state.hold = false;
620 if match emit {
621 VTEmit::Ground => unreachable!(),
622 VTEmit::Dcs => cb.event(VTEvent::DcsCancel),
623 VTEmit::Osc => cb.event(VTEvent::OscCancel),
624 }
625 .abort()
626 {
627 return i + 1;
628 }
629 }
630 };
631 i += 1;
632 }
633
634 if state.hold {
636 self.held_byte = Some(0x1b);
637 }
638
639 if let Some(emit) = state.current_emit.take() {
640 let range = &input[state.buffer_idx..input.len() - state.hold as usize];
641 if !range.is_empty() {
642 match emit {
643 VTEmit::Ground => cb.event(VTEvent::Raw(range)),
644 VTEmit::Dcs => cb.event(VTEvent::DcsData(range)),
645 VTEmit::Osc => cb.event(VTEvent::OscData(range)),
646 };
647 }
648 };
649
650 input.len()
652 }
653
654 pub fn is_ground(&self) -> bool {
656 self.st == State::Ground
657 }
658
659 pub fn idle(&mut self) -> Option<VTEvent<'static>> {
663 match self.st {
664 State::Escape => {
665 self.st = State::Ground;
666 Some(VTEvent::C0(ESC))
667 }
668 State::EscInt => {
669 self.st = State::Ground;
670 if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
671 None
672 } else {
673 Some(invalid!(self.priv_prefix, self.ints))
674 }
675 }
676 State::EscSs2 | State::EscSs3 => {
677 if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
678 self.st = State::Ground;
679 None
680 } else {
681 let c = match self.st {
682 State::EscSs2 => SS2,
683 State::EscSs3 => SS3,
684 _ => unreachable!(),
685 };
686 self.st = State::Ground;
687 Some(invalid!(c))
688 }
689 }
690 _ => None,
691 }
692 }
693
694 fn push_with(&mut self, b: u8) -> VTAction {
695 use State::*;
696 match self.st {
697 Ground => self.on_ground(b),
698 Escape => self.on_escape(b),
699 EscInt => self.on_esc_int(b),
700 EscSs2 => self.on_esc_ss2(b),
701 EscSs3 => self.on_esc_ss3(b),
702
703 CsiEntry => self.on_csi_entry(b),
704 CsiParam => self.on_csi_param(b),
705 CsiInt => self.on_csi_int(b),
706 CsiIgnore => self.on_csi_ignore(b),
707
708 DcsEntry => self.on_dcs_entry(b),
709 DcsParam => self.on_dcs_param(b),
710 DcsInt => self.on_dcs_int(b),
711 DcsIgnore => self.on_dcs_ignore(b),
712 DcsIgnoreEsc => self.on_dcs_ignore_esc(b),
713 DcsPassthrough => self.on_dcs_pass(b),
714 DcsEsc => self.on_dcs_esc(b),
715
716 OscString => self.on_osc_string(b),
717 OscEsc => self.on_osc_esc(b),
718
719 SosPmApcString => self.on_spa_string(b),
720 SpaEsc => self.on_spa_esc(b),
721 }
722 }
723
724 pub fn finish<F: FnMut(VTEvent)>(&mut self, _cb: &mut F) {
725 self.reset_collectors();
726 self.st = State::Ground;
727
728 }
730
731 fn clear_hdr_collectors(&mut self) {
736 self.ints.clear();
737 self.params.clear();
738 self.cur_param.clear();
739 self.priv_prefix = None;
740 }
741
742 fn reset_collectors(&mut self) {
743 self.clear_hdr_collectors();
744 }
745
746 fn next_param(&mut self) {
747 self.params.push(std::mem::take(&mut self.cur_param));
748 }
749
750 fn finish_params_if_any(&mut self) {
751 if !self.cur_param.is_empty() || !self.params.is_empty() {
752 self.next_param();
753 }
754 }
755
756 fn emit_csi(&mut self, final_byte: u8) -> VTAction {
757 self.finish_params_if_any();
758
759 let mut borrowed: SmallVec<[&[u8]; 4]> = SmallVec::new();
761 borrowed.extend(self.params.iter().map(|v| v.as_slice()));
762
763 let privp = self.priv_prefix.take();
764 VTAction::Event(VTEvent::Csi(CSI {
765 private: privp,
766 params: ParamBuf {
767 params: &self.params,
768 },
769 intermediates: self.ints,
770 final_byte,
771 }))
772 }
773
774 fn dcs_start(&mut self, final_byte: u8) -> VTAction {
775 self.finish_params_if_any();
776
777 let privp = self.priv_prefix.take();
778 VTAction::Event(VTEvent::DcsStart(DCS {
779 private: privp,
780 params: ParamBuf {
781 params: &self.params,
782 },
783 intermediates: self.ints,
784 final_byte,
785 }))
786 }
787
788 fn on_ground(&mut self, b: u8) -> VTAction {
793 match b {
794 ESC => {
795 self.clear_hdr_collectors();
796 self.st = State::Escape;
797 VTAction::None
798 }
799 DEL => VTAction::Event(VTEvent::C0(DEL)),
800 c if is_c0(c) => VTAction::Event(VTEvent::C0(c)),
801 p if is_printable(p) => VTAction::Buffer(VTEmit::Ground),
802 _ => VTAction::Buffer(VTEmit::Ground), }
804 }
805
806 fn on_escape(&mut self, b: u8) -> VTAction {
807 use State::*;
808 match b {
809 CAN | SUB => {
810 self.st = Ground;
811 if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
812 VTAction::None
813 } else {
814 VTAction::Event(invalid!(b))
815 }
816 }
817 DEL => {
820 self.st = Ground;
821 if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
822 VTAction::None
823 } else {
824 VTAction::Event(invalid!(b))
825 }
826 }
827 c if is_intermediate(c) => {
828 if self.ints.push(c) {
829 self.st = EscInt;
830 } else {
831 self.st = Ground;
832 }
833 VTAction::None
834 }
835 c if is_priv(c) => {
836 self.priv_prefix = Some(c);
837 self.st = EscInt;
838 VTAction::None
839 }
840 CSI => {
841 if INTEREST & VT_PARSER_INTEREST_CSI == 0 {
842 self.st = CsiIgnore;
843 } else {
844 self.st = CsiEntry;
845 }
846 VTAction::None
847 }
848 DCS => {
849 if INTEREST & VT_PARSER_INTEREST_DCS == 0 {
850 self.st = DcsIgnore;
851 } else {
852 self.st = DcsEntry;
853 }
854 VTAction::None
855 }
856 OSC => {
857 self.st = OscString;
858 VTAction::Event(VTEvent::OscStart)
859 }
860 SS2 => {
861 self.st = EscSs2;
862 VTAction::None
863 }
864 SS3 => {
865 self.st = EscSs3;
866 VTAction::None
867 }
868 SOS | PM | APC => {
869 self.st = State::SosPmApcString;
870 VTAction::None
871 }
872 c if is_final(c) || is_digit(c) => {
873 self.st = Ground;
874 VTAction::Event(VTEvent::Esc(Esc {
875 intermediates: self.ints,
876 private: self.priv_prefix.take(),
877 final_byte: c,
878 }))
879 }
880 ESC => {
881 VTAction::Event(VTEvent::C0(ESC))
883 }
884 _ => {
885 self.st = Ground;
886 if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
887 VTAction::None
888 } else {
889 VTAction::Event(invalid!(b))
890 }
891 }
892 }
893 }
894
895 fn on_esc_int(&mut self, b: u8) -> VTAction {
896 use State::*;
897 match b {
898 CAN | SUB => {
899 self.st = Ground;
900 if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
901 VTAction::None
902 } else {
903 VTAction::Event(invalid!(self.priv_prefix, self.ints, b))
904 }
905 }
906 DEL => {
909 self.st = Ground;
910 if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
911 VTAction::None
912 } else {
913 VTAction::Event(invalid!(self.priv_prefix, self.ints, b))
914 }
915 }
916 c if is_intermediate(c) => {
917 if !self.ints.push(c) {
918 self.st = Ground;
919 if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
920 VTAction::None
921 } else {
922 VTAction::Event(invalid!(self.priv_prefix, self.ints, b))
923 }
924 } else {
925 VTAction::None
926 }
927 }
928 c if is_final(c) || is_digit(c) => {
929 self.st = Ground;
930 VTAction::Event(VTEvent::Esc(Esc {
931 intermediates: self.ints,
932 private: self.priv_prefix.take(),
933 final_byte: c,
934 }))
935 }
936 ESC => {
939 self.st = Escape;
940 if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
941 VTAction::None
942 } else {
943 VTAction::Event(invalid!(self.priv_prefix, self.ints))
944 }
945 }
946 c => {
947 self.st = Ground;
948 if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
949 VTAction::None
950 } else {
951 VTAction::Event(invalid!(self.priv_prefix, self.ints, c))
952 }
953 }
954 }
955 }
956
957 fn on_esc_ss2(&mut self, b: u8) -> VTAction {
958 use State::*;
959 self.st = Ground;
960 match b {
961 CAN | SUB => {
962 if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
963 VTAction::None
964 } else {
965 VTAction::Event(invalid!(SS2, b))
966 }
967 }
968 ESC => {
971 self.st = Escape;
972 if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
973 VTAction::None
974 } else {
975 VTAction::Event(invalid!(SS2))
976 }
977 }
978 c => VTAction::Event(VTEvent::Ss2(SS2 { char: c })),
979 }
980 }
981
982 fn on_esc_ss3(&mut self, b: u8) -> VTAction {
983 use State::*;
984 self.st = Ground;
985 match b {
986 CAN | SUB => {
987 if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
988 VTAction::None
989 } else {
990 VTAction::Event(invalid!(SS3, b))
991 }
992 }
993 ESC => {
996 self.st = Escape;
997 if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
998 VTAction::None
999 } else {
1000 VTAction::Event(invalid!(SS3))
1001 }
1002 }
1003 c => VTAction::Event(VTEvent::Ss3(SS3 { char: c })),
1004 }
1005 }
1006
1007 fn on_csi_entry(&mut self, b: u8) -> VTAction {
1009 use State::*;
1010 match b {
1011 CAN | SUB => {
1012 self.st = Ground;
1013 VTAction::None
1014 }
1015 DEL => VTAction::None,
1016 ESC => {
1017 self.st = Escape;
1018 VTAction::None
1019 }
1020 c if is_priv(c) => {
1021 self.priv_prefix = Some(c);
1022 self.st = CsiParam;
1023 VTAction::None
1024 }
1025 d if is_digit(d) => {
1026 self.cur_param.push(d);
1027 self.st = CsiParam;
1028 VTAction::None
1029 }
1030 b';' => {
1031 self.next_param();
1032 self.st = CsiParam;
1033 VTAction::None
1034 }
1035 b':' => {
1036 self.cur_param.push(b':');
1037 self.st = CsiParam;
1038 VTAction::None
1039 }
1040 c if is_intermediate(c) => {
1041 if self.ints.push(c) {
1042 self.st = CsiInt;
1043 } else {
1044 self.st = Ground;
1045 }
1046 VTAction::None
1047 }
1048 c if is_final(c) => {
1049 self.st = Ground;
1050 self.emit_csi(c)
1051 }
1052 _ => {
1053 self.st = CsiIgnore;
1054 VTAction::None
1055 }
1056 }
1057 }
1058
1059 fn on_csi_param(&mut self, b: u8) -> VTAction {
1060 use State::*;
1061 match b {
1062 CAN | SUB => {
1063 self.st = Ground;
1064 VTAction::None
1065 }
1066 DEL => VTAction::None,
1067 ESC => {
1068 self.st = Escape;
1069 VTAction::None
1070 }
1071 d if is_digit(d) => {
1072 self.cur_param.push(d);
1073 VTAction::None
1074 }
1075 b';' => {
1076 self.next_param();
1077 VTAction::None
1078 }
1079 b':' => {
1080 self.cur_param.push(b':');
1081 VTAction::None
1082 }
1083 c if is_intermediate(c) => {
1084 if self.ints.push(c) {
1085 self.st = CsiInt;
1086 } else {
1087 self.st = Ground;
1088 }
1089 VTAction::None
1090 }
1091 c if is_final(c) => {
1092 self.st = Ground;
1093 self.emit_csi(c)
1094 }
1095 _ => {
1096 self.st = CsiIgnore;
1097 VTAction::None
1098 }
1099 }
1100 }
1101
1102 fn on_csi_int(&mut self, b: u8) -> VTAction {
1103 use State::*;
1104 match b {
1105 CAN | SUB => {
1106 self.st = Ground;
1107 VTAction::None
1108 }
1109 DEL => VTAction::None,
1110 ESC => {
1111 self.st = Escape;
1112 VTAction::None
1113 }
1114 c if is_intermediate(c) => {
1115 if self.ints.push(c) {
1116 self.st = CsiInt;
1117 } else {
1118 self.st = Ground;
1119 }
1120 VTAction::None
1121 }
1122 c if is_final(c) => {
1123 self.st = Ground;
1124 self.emit_csi(c)
1125 }
1126 _ => {
1127 self.st = CsiIgnore;
1128 VTAction::None
1129 }
1130 }
1131 }
1132
1133 fn on_csi_ignore(&mut self, b: u8) -> VTAction {
1134 use State::*;
1135 match b {
1136 CAN | SUB => {
1137 self.st = Ground;
1138 VTAction::None
1139 }
1140 DEL => VTAction::None,
1141 ESC => {
1142 self.st = Escape;
1143 VTAction::None
1144 }
1145 c if is_final(c) => {
1146 self.st = Ground;
1147 VTAction::None
1148 }
1149 _ => VTAction::None,
1150 }
1151 }
1152
1153 fn on_dcs_entry(&mut self, b: u8) -> VTAction {
1155 use State::*;
1156 match b {
1157 CAN | SUB => {
1158 self.st = Ground;
1159 VTAction::None
1160 }
1161 DEL => VTAction::None,
1162 ESC => {
1163 self.st = Escape;
1164 VTAction::None
1165 }
1166 c if is_priv(c) => {
1167 self.priv_prefix = Some(c);
1168 self.st = DcsParam;
1169 VTAction::None
1170 }
1171 d if is_digit(d) => {
1172 self.cur_param.push(d);
1173 self.st = DcsParam;
1174 VTAction::None
1175 }
1176 b';' => {
1177 self.next_param();
1178 self.st = DcsParam;
1179 VTAction::None
1180 }
1181 b':' => {
1182 self.st = DcsIgnore;
1183 VTAction::None
1184 }
1185 c if is_intermediate(c) => {
1186 if self.ints.push(c) {
1187 self.st = DcsInt;
1188 } else {
1189 self.st = Ground;
1190 }
1191 VTAction::None
1192 }
1193 c if is_final(c) => {
1194 self.st = DcsPassthrough;
1195 self.dcs_start(c)
1196 }
1197 _ => {
1198 self.st = DcsIgnore;
1199 VTAction::None
1200 }
1201 }
1202 }
1203
1204 fn on_dcs_param(&mut self, b: u8) -> VTAction {
1205 use State::*;
1206 match b {
1207 CAN | SUB => {
1208 self.st = Ground;
1209 VTAction::None
1210 }
1211 DEL => VTAction::None,
1212 ESC => {
1213 self.st = Escape;
1214 VTAction::None
1215 }
1216 d if is_digit(d) => {
1217 self.cur_param.push(d);
1218 VTAction::None
1219 }
1220 b';' => {
1221 self.next_param();
1222 VTAction::None
1223 }
1224 b':' => {
1225 self.st = DcsIgnore;
1226 VTAction::None
1227 }
1228 c if is_intermediate(c) => {
1229 if self.ints.push(c) {
1230 self.st = DcsInt;
1231 } else {
1232 self.st = Ground;
1233 }
1234 self.st = DcsInt;
1235 VTAction::None
1236 }
1237 c if is_final(c) => {
1238 self.st = DcsPassthrough;
1239 self.dcs_start(c)
1240 }
1241 _ => {
1242 self.st = DcsIgnore;
1243 VTAction::None
1244 }
1245 }
1246 }
1247
1248 fn on_dcs_int(&mut self, b: u8) -> VTAction {
1249 use State::*;
1250 match b {
1251 CAN | SUB => {
1252 self.st = Ground;
1253 VTAction::None
1254 }
1255 DEL => VTAction::None,
1256 ESC => {
1257 self.st = Escape;
1258 VTAction::None
1259 }
1260 c if is_intermediate(c) => {
1261 if self.ints.push(c) {
1262 self.st = DcsInt;
1263 } else {
1264 self.st = Ground;
1265 }
1266 VTAction::None
1267 }
1268 c if is_final(c) || is_digit(c) || c == b':' || c == b';' => {
1269 self.st = DcsPassthrough;
1270 self.dcs_start(c)
1271 }
1272 _ => {
1273 self.st = DcsIgnore;
1274 VTAction::None
1275 }
1276 }
1277 }
1278
1279 fn on_dcs_ignore(&mut self, b: u8) -> VTAction {
1280 use State::*;
1281 match b {
1282 CAN | SUB => {
1283 self.st = Ground;
1284 VTAction::None
1285 }
1286 DEL => VTAction::None,
1287 ESC => {
1288 self.st = DcsIgnoreEsc;
1289 VTAction::None
1290 }
1291 _ => VTAction::None,
1292 }
1293 }
1294
1295 fn on_dcs_ignore_esc(&mut self, b: u8) -> VTAction {
1296 use State::*;
1297 match b {
1298 CAN | SUB => {
1299 self.st = Ground;
1300 VTAction::None
1301 }
1302 ST_FINAL => {
1303 self.st = Ground;
1304 VTAction::None
1305 }
1306 DEL => VTAction::None,
1307 ESC => VTAction::None,
1308 _ => {
1309 self.st = DcsIgnore;
1310 VTAction::None
1311 }
1312 }
1313 }
1314
1315 fn on_dcs_pass(&mut self, b: u8) -> VTAction {
1316 use State::*;
1317 match b {
1318 CAN | SUB => {
1319 self.st = Ground;
1320 VTAction::Cancel(VTEmit::Dcs)
1321 }
1322 DEL => VTAction::None,
1323 ESC => {
1324 self.st = DcsEsc;
1325 VTAction::Hold(VTEmit::Dcs)
1326 }
1327 _ => VTAction::Buffer(VTEmit::Dcs),
1328 }
1329 }
1330
1331 fn on_dcs_esc(&mut self, b: u8) -> VTAction {
1332 use State::*;
1333 match b {
1334 ST_FINAL => {
1335 self.st = Ground;
1336 VTAction::End(VTEnd::Dcs)
1337 }
1338 DEL => VTAction::None,
1339 ESC => {
1340 VTAction::Hold(VTEmit::Dcs)
1342 }
1343 _ => {
1344 self.st = DcsPassthrough;
1346 VTAction::Buffer(VTEmit::Dcs)
1347 }
1348 }
1349 }
1350
1351 fn on_osc_string(&mut self, b: u8) -> VTAction {
1353 use State::*;
1354 match b {
1355 CAN | SUB => {
1356 self.st = Ground;
1357 VTAction::Cancel(VTEmit::Osc)
1358 }
1359 DEL => VTAction::None,
1360 BEL => {
1361 self.st = Ground;
1362 VTAction::End(VTEnd::Osc { used_bel: true })
1363 }
1364 ESC => {
1365 self.st = OscEsc;
1366 VTAction::Hold(VTEmit::Osc)
1367 }
1368 p if is_printable(p) => VTAction::Buffer(VTEmit::Osc),
1369 _ => VTAction::None, }
1371 }
1372
1373 fn on_osc_esc(&mut self, b: u8) -> VTAction {
1374 use State::*;
1375 match b {
1376 ST_FINAL => {
1377 self.st = Ground;
1378 VTAction::End(VTEnd::Osc { used_bel: false })
1379 } ESC => VTAction::Hold(VTEmit::Osc),
1381 DEL => VTAction::None,
1382 _ => {
1383 self.st = OscString;
1384 VTAction::Buffer(VTEmit::Osc)
1385 }
1386 }
1387 }
1388
1389 fn on_spa_string(&mut self, b: u8) -> VTAction {
1391 use State::*;
1392 match b {
1393 CAN | SUB => {
1394 self.st = Ground;
1395 VTAction::None
1396 }
1397 DEL => VTAction::None,
1398 ESC => {
1399 self.st = SpaEsc;
1400 VTAction::None
1401 }
1402 _ => VTAction::None,
1403 }
1404 }
1405
1406 fn on_spa_esc(&mut self, b: u8) -> VTAction {
1407 use State::*;
1408 match b {
1409 ST_FINAL => {
1410 self.st = Ground;
1411 VTAction::None
1412 }
1413 DEL => VTAction::None,
1414 ESC => {
1415 VTAction::None
1417 }
1418 _ => {
1419 self.st = State::SosPmApcString;
1420 VTAction::None
1421 }
1422 }
1423 }
1424}
1425
1426#[cfg(test)]
1427mod tests {
1428 use crate::event::VTOwnedEvent;
1429
1430 use super::*;
1431 use pretty_assertions::assert_eq;
1432
1433 #[test]
1434 fn test_edge_cases() {
1435 let mut result = String::new();
1437 VTPushParser::decode_buffer(&[], |e| result.push_str(&format!("{e:?}\n")));
1438 assert_eq!(result.trim(), "");
1439
1440 let mut result = String::new();
1442 VTPushParser::decode_buffer(b"\x1b", |e| result.push_str(&format!("{e:?}\n")));
1443 assert_eq!(result.trim(), "");
1444
1445 let mut result = String::new();
1447 VTPushParser::decode_buffer(b"\x1b[", |e| result.push_str(&format!("{e:?}\n")));
1448 assert_eq!(result.trim(), "");
1449
1450 let mut result = String::new();
1452 VTPushParser::decode_buffer(b"\x1bP", |e| result.push_str(&format!("{e:?}\n")));
1453 assert_eq!(result.trim(), "");
1454
1455 let mut result = String::new();
1457 VTPushParser::decode_buffer(b"\x1b]", |e| result.push_str(&format!("{e:?}\n")));
1458 assert_eq!(result.trim(), "OscStart");
1459 }
1460
1461 #[test]
1462 fn test_streaming_behavior() {
1463 let mut parser = VTPushParser::new(); let mut result = String::new();
1466 let mut callback = |vt_input: VTEvent<'_>| {
1467 result.push_str(&format!("{vt_input:?}\n"));
1468 };
1469
1470 parser.feed_with(b"\x1bP1;2;3 |", &mut callback);
1472 parser.feed_with(b"data", &mut callback);
1473 parser.feed_with(b" more", &mut callback);
1474 parser.feed_with(b"\x1b\\", &mut callback);
1475
1476 assert_eq!(
1477 result.trim(),
1478 "DcsStart('1', '2', '3', ' ', |)\nDcsData('data')\nDcsData(' more')\nDcsEnd('')"
1479 );
1480 }
1481
1482 #[test]
1483 fn test_dcs_payload_passthrough() {
1484 let dcs_cases: &[(&[u8], &str)] = &[
1492 (b"\x1bPq\x1b[38:2:12:34:56m\x1b\\", "<ESC>[38:2:12:34:56m"),
1494 (b"\x1bPq\x1b[48:2:0:0:0m;xyz\x1b\\", "<ESC>[48:2:0:0:0m;xyz"),
1496 (
1498 b"\x1bP1$r\x1b[38:2:10:20:30;58:2::200:100:0m\x1b\\",
1499 "<ESC>[38:2:10:20:30;58:2::200:100:0m",
1500 ),
1501 (
1503 b"\x1bPqABC\x1b\x1bDEF\x1bXG\x1b\\",
1504 "ABC<ESC><ESC>DEF<ESC>XG",
1505 ),
1506 (b"\x1bPqDATA\x07MORE\x1b\\", "DATA<BEL>MORE"),
1508 (b"\x1bP!|\x1b[38:5:208m\x1b\\", "<ESC>[38:5:208m"),
1510 (b"\x1bP>|Hello world\x1b\\", "Hello world"),
1512 (
1514 b"\x1bPq\x1b[38:2:1:2:3m\x1b[48:5:17m\x1b\\",
1515 "<ESC>[38:2:1:2:3m<ESC>[48:5:17m",
1516 ),
1517 (
1519 b"\x1bPq\x1b[58:2::000:007:042m\x1b\\",
1520 "<ESC>[58:2::000:007:042m",
1521 ),
1522 ];
1523
1524 for (input, expected_body) in dcs_cases {
1525 let events = collect_owned_events(input);
1526
1527 let mut actual_body = Vec::new();
1529 for event in &events {
1530 match event {
1531 VTOwnedEvent::DcsData(data) | VTOwnedEvent::DcsEnd(data) => {
1532 actual_body.extend(data);
1533 }
1534 _ => {}
1535 }
1536 }
1537
1538 let actual_body = String::from_utf8(actual_body).unwrap();
1539 let actual_body = actual_body
1540 .replace("\x1b", "<ESC>")
1541 .replace("\x07", "<BEL>");
1542
1543 assert_eq!(
1544 actual_body, *expected_body,
1545 "DCS payload mismatch for input {:?}. Full events: {:#?}",
1546 input, events
1547 );
1548 }
1549 }
1550
1551 fn collect_events(input: &[u8]) -> Vec<String> {
1552 let mut out = Vec::new();
1553 let mut p = VTPushParser::new();
1554 p.feed_with(input, |ev: VTEvent| out.push(format!("{ev:?}")));
1555 out
1556 }
1557
1558 fn collect_owned_events(input: &[u8]) -> Vec<VTOwnedEvent> {
1559 let mut out = Vec::new();
1560 let mut p = VTPushParser::new();
1561 p.feed_with(input, &mut |ev: VTEvent| out.push(ev.to_owned()));
1562 out
1563 }
1564
1565 #[test]
1566 fn dcs_esc_esc_del() {
1567 let ev = collect_events(b"\x1bP1;2;3|\x1b\x1b\x7fdata\x1b\\");
1569 eprintln!("{ev:?}");
1571 }
1572
1573 #[test]
1574 fn dcs_header_with_colon_is_ignored_case1() {
1575 let ev = collect_events(b"\x1bP1:2qHELLO\x1b\\");
1577 assert!(ev.iter().all(|e| !e.starts_with("DcsStart")), "{ev:#?}");
1579 }
1580
1581 #[test]
1582 fn dcs_header_with_colon_is_ignored_case2() {
1583 let ev = collect_events(b"\x1bP:1qDATA\x1b\\");
1585 assert!(ev.iter().all(|e| !e.starts_with("DcsStart")), "{ev:#?}");
1586 }
1587
1588 #[test]
1589 fn dcs_header_with_colon_is_ignored_case3() {
1590 let ev = collect_events(b"\x1bP12:34!qPAYLOAD\x1b\\");
1592 assert!(ev.iter().all(|e| !e.starts_with("DcsStart")), "{ev:#?}");
1593 }
1594
1595 #[test]
1596 fn osc_aborted_by_can_mid_body() {
1597 let mut s = Vec::new();
1599 s.extend_from_slice(b"\x1b]0;Title");
1600 s.push(CAN);
1601 s.extend_from_slice(b"more\x07");
1602
1603 let ev = collect_debug(&s);
1604
1605 assert!(ev.iter().any(|e| e.starts_with("OscStart")), "{ev:#?}");
1610 assert!(!ev.iter().any(|e| e.starts_with("OscData")), "{ev:#?}");
1611 assert!(!ev.iter().any(|e| e.starts_with("OscEnd")), "{ev:#?}");
1612 }
1613
1614 #[test]
1615 fn osc_aborted_by_sub_before_terminator() {
1616 let mut s = Vec::new();
1617 s.extend_from_slice(b"\x1b]52;c;YWJjZA==");
1618 s.push(SUB); s.extend_from_slice(b"\x1b\\"); let ev = collect_debug(&s);
1622 assert!(ev.iter().any(|e| e.starts_with("OscStart")), "{ev:#?}");
1626 assert!(!ev.iter().any(|e| e.starts_with("OscData")), "{ev:#?}");
1627 assert!(!ev.iter().any(|e| e.starts_with("OscEnd")), "{ev:#?}");
1628 }
1629
1630 fn collect_debug(input: &[u8]) -> Vec<String> {
1632 let mut out = Vec::new();
1633 let mut p = VTPushParser::new();
1634 p.feed_with(input, |ev: VTEvent| out.push(format!("{ev:?}")));
1635 out
1636 }
1637
1638 #[test]
1639 fn dcs_aborted_by_can_before_body() {
1640 let mut s = Vec::new();
1642 s.extend_from_slice(b"\x1bPq"); s.push(CAN);
1644 s.extend_from_slice(b"IGNORED\x1b\\"); let ev = collect_debug(&s);
1647
1648 assert_eq!(ev.len(), 4, "{ev:#?}");
1649 assert_eq!(ev[0], "DcsStart('', q)");
1650 assert_eq!(ev[1], "DcsCancel");
1651 assert_eq!(ev[2], "Raw('IGNORED')");
1652 assert_eq!(ev[3], "Esc('', \\)");
1653 }
1654
1655 #[test]
1656 fn dcs_aborted_by_can_mid_body() {
1657 let mut s = Vec::new();
1659 s.extend_from_slice(b"\x1bPqABC");
1660 s.push(CAN);
1661 s.extend_from_slice(b"MORE\x1b\\"); let ev = collect_debug(&s);
1664
1665 assert_eq!(ev.len(), 4, "{ev:#?}");
1666 assert_eq!(ev[0], "DcsStart('', q)");
1667 assert_eq!(ev[1], "DcsCancel");
1668 assert_eq!(ev[2], "Raw('MORE')");
1669 assert_eq!(ev[3], "Esc('', \\)");
1670 }
1671
1672 #[test]
1675 fn spa_aborted_by_can_is_ignored() {
1676 let mut s = Vec::new();
1678 s.extend_from_slice(b"\x1b_hello");
1679 s.push(CAN);
1680 s.extend_from_slice(b"world\x1b\\");
1681
1682 let ev = collect_debug(&s);
1683 assert_eq!(ev.len(), 2, "{ev:#?}");
1684 assert_eq!(ev[0], "Raw('world')");
1685 assert_eq!(ev[1], "Esc('', \\)");
1686 }
1687
1688 #[test]
1689 fn spa_sub_aborts_too() {
1690 let mut s = Vec::new();
1691 s.extend_from_slice(b"\x1bXhello");
1692 s.push(SUB);
1693 s.extend_from_slice(b"world\x1b\\");
1694 let ev = collect_debug(&s);
1695 assert_eq!(ev.len(), 2, "{ev:#?}");
1696 assert_eq!(ev[0], "Raw('world')");
1697 assert_eq!(ev[1], "Esc('', \\)");
1698 }
1699
1700 #[test]
1703 fn can_in_ground_is_c0() {
1704 let mut s = Vec::new();
1705 s.extend_from_slice(b"abc");
1706 s.push(CAN);
1707 s.extend_from_slice(b"def");
1708 let ev = collect_debug(&s);
1709 assert_eq!(ev.len(), 3, "{ev:#?}");
1711 assert_eq!(ev[0], "Raw('abc')");
1712 assert_eq!(ev[1], "C0(18)");
1713 assert_eq!(ev[2], "Raw('def')");
1714 }
1715
1716 #[test]
1719 fn three_byte_sequences_capturable() {
1720 let mut bytes = vec![];
1721 for i in 0..=0xFFFFFF_u32 {
1722 bytes.clear();
1723 let test_bytes = i.to_le_bytes();
1724 let test_bytes = &test_bytes[..3];
1725 if test_bytes.iter().any(|b| b == &0) {
1726 continue;
1727 }
1728 if test_bytes[0] == 0x1b && matches!(test_bytes[1], CSI | DCS | OSC | APC | PM | SOS) {
1729 continue;
1730 }
1731 if test_bytes[1] == 0x1b && matches!(test_bytes[2], CSI | DCS | OSC | APC | PM | SOS) {
1732 continue;
1733 }
1734
1735 let mut parser = VTPushParser::<VT_PARSER_INTEREST_ALL>::new_with();
1736 parser.feed_with(test_bytes, |event: VTEvent| {
1737 let mut chunk = [0_u8; 3];
1738 let b = event.encode(&mut chunk).unwrap_or_else(|_| {
1739 panic!("Failed to encode event {test_bytes:X?} -> {event:?}")
1740 });
1741 bytes.extend_from_slice(&chunk[..b]);
1742 });
1743 if let Some(event) = parser.idle() {
1744 let mut chunk = [0_u8; 3];
1745 let b = event.encode(&mut chunk).unwrap_or_else(|_| {
1746 panic!("Failed to encode event {test_bytes:X?} -> {event:?}")
1747 });
1748 bytes.extend_from_slice(&chunk[..b]);
1749 }
1750
1751 if bytes.len() != 3 || bytes != test_bytes {
1752 eprintln!("Failed to parse:");
1753 parser.feed_with(test_bytes, |event: VTEvent| {
1754 eprintln!("{event:?}");
1755 });
1756 if let Some(event) = parser.idle() {
1757 eprintln!("{event:?}");
1758 }
1759 assert_eq!(bytes, test_bytes, "{test_bytes:X?} -> {bytes:X?}");
1760 }
1761 }
1762 }
1763}