From 255c2fee44f2d94184d458713d14803c3e2f5a08 Mon Sep 17 00:00:00 2001 From: Boaz Yaniv Date: Sun, 4 Aug 2024 22:13:08 +0900 Subject: [PATCH 1/6] Add support for custom parsing of APC, SOS and PM sequences. --- src/definitions.rs | 74 ++++++++++++++++++++++---------------------- src/lib.rs | 76 ++++++++++++++++++++++++++++++++++++++++++++++ src/table.rs | 10 +++--- 3 files changed, 119 insertions(+), 41 deletions(-) diff --git a/src/definitions.rs b/src/definitions.rs index 218c1eb..28cd746 100644 --- a/src/definitions.rs +++ b/src/definitions.rs @@ -19,7 +19,7 @@ pub enum State { #[default] Ground = 12, OscString = 13, - SosPmApcString = 14, + OpaqueString = 14, Utf8 = 15, } @@ -28,21 +28,34 @@ pub enum State { #[derive(Debug, Clone, Copy)] pub enum Action { None = 0, - Clear = 1, - Collect = 2, - CsiDispatch = 3, - EscDispatch = 4, - Execute = 5, - Hook = 6, - Ignore = 7, - OscEnd = 8, - OscPut = 9, - OscStart = 10, - Param = 11, - Print = 12, - Put = 13, - Unhook = 14, - BeginUtf8 = 15, + Collect = 1, + CsiDispatch = 2, + EscDispatch = 3, + Execute = 4, + Ignore = 5, + OscPut = 6, + Param = 7, + Print = 8, + Put = 9, + BeginUtf8 = 10, + OpaquePut = 11, + + // Actions that do not need to be packed as 4 bits in the state table + // Can have values higher than 16 + Clear = 16, + Hook = 17, + Unhook = 18, + OscStart = 19, + OscEnd = 20, + OpaqueStart = 21, + OpaqueEnd = 22, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum OpaqueSequenceKind { + Sos, + Pm, + Apc, } /// Unpack a u8 into a State and Action @@ -57,9 +70,9 @@ pub fn unpack(delta: u8) -> (State, Action) { unsafe { ( // State is stored in bottom 4 bits - mem::transmute(delta & 0x0f), + mem::transmute::(delta & 0x0f), // Action is stored in top 4 bits - mem::transmute(delta >> 4), + mem::transmute::(delta >> 4), ) } } @@ -75,8 +88,8 @@ mod tests { #[test] fn unpack_state_action() { - match unpack(0xee) { - (State::SosPmApcString, Action::Unhook) => (), + match unpack(0xaa) { + (State::Escape, Action::BeginUtf8) => (), _ => panic!("unpack failed"), } @@ -85,27 +98,16 @@ mod tests { _ => panic!("unpack failed"), } - match unpack(0xff) { - (State::Utf8, Action::BeginUtf8) => (), + match unpack(0xbf) { + (State::Utf8, Action::OpaquePut) => (), _ => panic!("unpack failed"), } } #[test] fn pack_state_action() { - match unpack(0xee) { - (State::SosPmApcString, Action::Unhook) => (), - _ => panic!("unpack failed"), - } - - match unpack(0x0f) { - (State::Utf8, Action::None) => (), - _ => panic!("unpack failed"), - } - - match unpack(0xff) { - (State::Utf8, Action::BeginUtf8) => (), - _ => panic!("unpack failed"), - } + assert_eq!(pack(State::Escape, Action::BeginUtf8), 0xaa); + assert_eq!(pack(State::Utf8, Action::None), 0x0f); + assert_eq!(pack(State::Utf8, Action::OpaquePut), 0xbf); } } diff --git a/src/lib.rs b/src/lib.rs index 31e2a31..aa006f8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -48,6 +48,7 @@ mod table; pub mod ansi; pub use params::{Params, ParamsIter}; +use crate::definitions::OpaqueSequenceKind; use definitions::{unpack, Action, State}; const MAX_INTERMEDIATES: usize = 2; @@ -187,6 +188,9 @@ impl Parser { State::OscString => { self.perform_action(performer, Action::OscEnd, byte); }, + State::OpaqueString => { + self.perform_action(performer, Action::OpaqueEnd, byte); + }, _ => (), } @@ -202,6 +206,9 @@ impl Parser { State::OscString => { self.perform_action(performer, Action::OscStart, byte); }, + State::OpaqueString => { + self.perform_action(performer, Action::OpaqueStart, byte); + }, _ => (), } @@ -364,6 +371,24 @@ impl Parser { Action::BeginUtf8 => self.process_utf8(performer, byte), Action::Ignore => (), Action::None => (), + + // APC Actions are checked last, since they are relatively rare + Action::OpaqueStart => { + let kind = match byte { + 0x58 => OpaqueSequenceKind::Sos, + 0x5e => OpaqueSequenceKind::Pm, + 0x5f => OpaqueSequenceKind::Apc, + + // Changes to OpaqueString state which trigger this action are only possible + // when one of the escape sequences above is detected (see Escape state changes + // in table.rs). Since there is no other way to reach this action with any other + // byte value, this branch is unreachable. + _ => unreachable!("invalid opaque sequence kind"), + }; + performer.opaque_start(kind) + }, + Action::OpaquePut => performer.opaque_put(byte), + Action::OpaqueEnd => performer.opaque_end(), } } } @@ -428,6 +453,20 @@ pub trait Perform { /// The `ignore` flag indicates that more than two intermediates arrived and /// subsequent characters were ignored. fn esc_dispatch(&mut self, _intermediates: &[u8], _ignore: bool, _byte: u8) {} + + /// The start of an opaque sequence (SOS, PM or APC) has been detected. + /// + /// The `kind` parameter indicates the type of sequence that was started. + /// + /// Until the opaque sequence ends (at which point `opaque_end` will be called), invalid + /// characters will be ignored while valid characters will be passed on to `opaque_put`. + fn opaque_start(&mut self, _kind: OpaqueSequenceKind) {} + + /// We've reached the end of the ongoing opaque sequence (SOS, PM or APC). + fn opaque_end(&mut self) {} + + /// A byte has been received as part of an ongoing opaque sequence. + fn opaque_put(&mut self, _byte: u8) {} } #[cfg(all(test, feature = "no_std"))] @@ -460,6 +499,9 @@ mod tests { DcsHook(Vec>, Vec, bool, char), DcsPut(u8), DcsUnhook, + OpaqueStart(OpaqueSequenceKind), + OpaquePut(u8), + OpaqueEnd, } impl Perform for Dispatcher { @@ -492,6 +534,18 @@ mod tests { fn unhook(&mut self) { self.dispatched.push(Sequence::DcsUnhook); } + + fn opaque_start(&mut self, _kind: OpaqueSequenceKind) { + self.dispatched.push(Sequence::OpaqueStart(_kind)); + } + + fn opaque_put(&mut self, byte: u8) { + self.dispatched.push(Sequence::OpaquePut(byte)); + } + + fn opaque_end(&mut self) { + self.dispatched.push(Sequence::OpaqueEnd); + } } #[test] @@ -628,6 +682,28 @@ mod tests { } } + #[test] + fn parse_apc() { + const INPUT: &[u8] = b"\x1b_abc\x1b\\"; + + // Test with ESC \ terminator. + + let mut dispatcher = Dispatcher::default(); + let mut parser = Parser::new(); + + for byte in INPUT { + parser.advance(&mut dispatcher, *byte); + } + assert_eq!(dispatcher.dispatched.len(), 6); + assert_eq!(dispatcher.dispatched[0..5], vec![ + Sequence::OpaqueStart(OpaqueSequenceKind::Apc), + Sequence::OpaquePut(b'a'), + Sequence::OpaquePut(b'b'), + Sequence::OpaquePut(b'c'), + Sequence::OpaqueEnd, + ]) + } + #[test] fn exceed_max_buffer_size() { static NUM_BYTES: usize = MAX_OSC_RAW + 100; diff --git a/src/table.rs b/src/table.rs index f2c0105..31628b1 100644 --- a/src/table.rs +++ b/src/table.rs @@ -44,9 +44,9 @@ generate_state_changes!(state_changes, { 0x5b => (CsiEntry, None), 0x5d => (OscString, None), 0x50 => (DcsEntry, None), - 0x58 => (SosPmApcString, None), - 0x5e => (SosPmApcString, None), - 0x5f => (SosPmApcString, None), + 0x58 => (OpaqueString, None), + 0x5e => (OpaqueString, None), + 0x5f => (OpaqueString, None), }, EscapeIntermediate { @@ -152,11 +152,11 @@ generate_state_changes!(state_changes, { 0x9c => (Ground, None), }, - SosPmApcString { + OpaqueString { 0x00..=0x17 => (Anywhere, Ignore), 0x19 => (Anywhere, Ignore), 0x1c..=0x1f => (Anywhere, Ignore), - 0x20..=0x7f => (Anywhere, Ignore), + 0x20..=0x7f => (Anywhere, OpaquePut), 0x9c => (Ground, None), }, From 8777a53b0bfb95f0c2724f8d926574f4be29f1db Mon Sep 17 00:00:00 2001 From: Boaz Yaniv Date: Wed, 14 Aug 2024 12:44:31 +0900 Subject: [PATCH 2/6] Update src/definitions.rs Fix typo (case) Co-authored-by: Christian Duerr --- src/definitions.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/definitions.rs b/src/definitions.rs index 28cd746..b5ea1d5 100644 --- a/src/definitions.rs +++ b/src/definitions.rs @@ -41,7 +41,7 @@ pub enum Action { OpaquePut = 11, // Actions that do not need to be packed as 4 bits in the state table - // Can have values higher than 16 + // can have values higher than 16. Clear = 16, Hook = 17, Unhook = 18, From 790e34e689eee202fa905856bad86fc61e7d86c8 Mon Sep 17 00:00:00 2001 From: Boaz Yaniv Date: Wed, 14 Aug 2024 12:45:04 +0900 Subject: [PATCH 3/6] Update src/lib.rs Fix typo (period) Co-authored-by: Christian Duerr --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index aa006f8..a82195f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -372,7 +372,7 @@ impl Parser { Action::Ignore => (), Action::None => (), - // APC Actions are checked last, since they are relatively rare + // APC Actions are checked last, since they are relatively rare. Action::OpaqueStart => { let kind = match byte { 0x58 => OpaqueSequenceKind::Sos, From 45061fd857ef46fb2959ea8ec21ca96744f21aea Mon Sep 17 00:00:00 2001 From: Boaz Yaniv Date: Thu, 17 Oct 2024 15:14:22 +0900 Subject: [PATCH 4/6] Split opauqe_*() functions into sos_*(), pm_*(), apc_*(). Split the `opaque_[start|put|end]()` set of functions into 3 different sets of functions for SOS, PM and APC sequences. --- src/lib.rs | 258 +++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 189 insertions(+), 69 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index a82195f..2090f40 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -90,6 +90,7 @@ pub struct Parser { osc_num_params: usize, ignoring: bool, utf8_parser: utf8::Parser, + opaque_sequence_kind: Option, } impl Parser { @@ -170,7 +171,7 @@ impl Parser { Action::None => (), action => { self.perform_action(performer, action, $arg); - }, + } } }; } @@ -179,18 +180,18 @@ impl Parser { State::Anywhere => { // Just run the action self.perform_action(performer, action, byte); - }, + } state => { match self.state { State::DcsPassthrough => { self.perform_action(performer, Action::Unhook, byte); - }, + } State::OscString => { self.perform_action(performer, Action::OscEnd, byte); - }, + } State::OpaqueString => { self.perform_action(performer, Action::OpaqueEnd, byte); - }, + } _ => (), } @@ -199,22 +200,22 @@ impl Parser { match state { State::CsiEntry | State::DcsEntry | State::Escape => { self.perform_action(performer, Action::Clear, byte); - }, + } State::DcsPassthrough => { self.perform_action(performer, Action::Hook, byte); - }, + } State::OscString => { self.perform_action(performer, Action::OscStart, byte); - }, + } State::OpaqueString => { self.perform_action(performer, Action::OpaqueStart, byte); - }, + } _ => (), } // Assume the new state self.state = state; - }, + } } } @@ -251,12 +252,12 @@ impl Parser { } performer.hook(self.params(), self.intermediates(), self.ignoring, byte as char); - }, + } Action::Put => performer.put(byte), Action::OscStart => { self.osc_raw.clear(); self.osc_num_params = 0; - }, + } Action::OscPut => { #[cfg(feature = "no_std")] { @@ -277,21 +278,21 @@ impl Parser { // First param is special - 0 to current byte index 0 => { self.osc_params[param_idx] = (0, idx); - }, + } // All other params depend on previous indexing _ => { let prev = self.osc_params[param_idx - 1]; let begin = prev.1; self.osc_params[param_idx] = (begin, idx); - }, + } } self.osc_num_params += 1; } else { self.osc_raw.push(byte); } - }, + } Action::OscEnd => { let param_idx = self.osc_num_params; let idx = self.osc_raw.len(); @@ -304,7 +305,7 @@ impl Parser { 0 => { self.osc_params[param_idx] = (0, idx); self.osc_num_params += 1; - }, + } // All other params depend on previous indexing _ => { @@ -312,10 +313,10 @@ impl Parser { let begin = prev.1; self.osc_params[param_idx] = (begin, idx); self.osc_num_params += 1; - }, + } } self.osc_dispatch(performer, byte); - }, + } Action::Unhook => performer.unhook(), Action::CsiDispatch => { if self.params.is_full() { @@ -330,10 +331,10 @@ impl Parser { self.ignoring, byte as char, ); - }, + } Action::EscDispatch => { performer.esc_dispatch(self.intermediates(), self.ignoring, byte); - }, + } Action::Collect => { if self.intermediate_idx == MAX_INTERMEDIATES { self.ignoring = true; @@ -341,7 +342,7 @@ impl Parser { self.intermediates[self.intermediate_idx] = byte; self.intermediate_idx += 1; } - }, + } Action::Param => { if self.params.is_full() { self.ignoring = true; @@ -359,7 +360,7 @@ impl Parser { self.param = self.param.saturating_mul(10); self.param = self.param.saturating_add((byte - b'0') as u16); } - }, + } Action::Clear => { // Reset everything on ESC/CSI/DCS entry self.intermediate_idx = 0; @@ -367,7 +368,7 @@ impl Parser { self.param = 0; self.params.clear(); - }, + } Action::BeginUtf8 => self.process_utf8(performer, byte), Action::Ignore => (), Action::None => (), @@ -385,10 +386,34 @@ impl Parser { // byte value, this branch is unreachable. _ => unreachable!("invalid opaque sequence kind"), }; - performer.opaque_start(kind) - }, - Action::OpaquePut => performer.opaque_put(byte), - Action::OpaqueEnd => performer.opaque_end(), + self.opaque_sequence_kind = Some(kind); + match kind { + OpaqueSequenceKind::Sos => performer.sos_start(), + OpaqueSequenceKind::Pm => performer.pm_start(), + OpaqueSequenceKind::Apc => performer.apc_start(), + } + } + Action::OpaquePut => { + match self.opaque_sequence_kind { + Some(OpaqueSequenceKind::Sos) => performer.sos_put(byte), + Some(OpaqueSequenceKind::Pm) => performer.pm_put(byte), + Some(OpaqueSequenceKind::Apc) => performer.apc_put(byte), + // This action is only triggered inside the OpaqueString state, which requires + // that the opaque_sequence_kind is set to a Some(x) value. + None => unreachable!("opaque sequence kind not set"), + } + } + Action::OpaqueEnd => { + match self.opaque_sequence_kind { + Some(OpaqueSequenceKind::Sos) => performer.sos_end(), + Some(OpaqueSequenceKind::Pm) => performer.pm_end(), + Some(OpaqueSequenceKind::Apc) => performer.apc_end(), + // This action is only triggered inside the OpaqueString state, which requires + // that the opaque_sequence_kind is set to a Some(x) value. + None => unreachable!("opaque sequence kind not set"), + } + self.opaque_sequence_kind = None; + } } } } @@ -445,8 +470,7 @@ pub trait Perform { _intermediates: &[u8], _ignore: bool, _action: char, - ) { - } + ) {} /// The final character of an escape sequence has arrived. /// @@ -454,19 +478,41 @@ pub trait Perform { /// subsequent characters were ignored. fn esc_dispatch(&mut self, _intermediates: &[u8], _ignore: bool, _byte: u8) {} - /// The start of an opaque sequence (SOS, PM or APC) has been detected. + /// The start of an SOS sequence has been detected. + /// + /// Until the SOS sequence ends (at which point `sos_end` will be called), invalid + /// characters will be ignored while valid characters will be passed on to `sos_put`. + fn sos_start(&mut self) {} + + /// A byte has been received as part of an ongoing SOS sequence. + fn sos_put(&mut self, _byte: u8) {} + + /// We've reached the end of the ongoing SOS sequence. + fn sos_end(&mut self) {} + + /// The start of a PM sequence has been detected. /// - /// The `kind` parameter indicates the type of sequence that was started. + /// Until the PM sequence ends (at which point `pm_end` will be called), invalid + /// characters will be ignored while valid characters will be passed on to `pm_put`. + fn pm_start(&mut self) {} + + /// A byte has been received as part of an ongoing PM sequence. + fn pm_put(&mut self, _byte: u8) {} + + /// We've reached the end of the ongoing PM sequence. + fn pm_end(&mut self) {} + + /// The start of an APC sequence has been detected. /// - /// Until the opaque sequence ends (at which point `opaque_end` will be called), invalid - /// characters will be ignored while valid characters will be passed on to `opaque_put`. - fn opaque_start(&mut self, _kind: OpaqueSequenceKind) {} + /// Until the APC sequence ends (at which point `apc_end` will be called), invalid + /// characters will be ignored while valid characters will be passed on to `apc_put`. + fn apc_start(&mut self) {} - /// We've reached the end of the ongoing opaque sequence (SOS, PM or APC). - fn opaque_end(&mut self) {} + /// A byte has been received as part of an ongoing APC sequence. + fn apc_put(&mut self, _byte: u8) {} - /// A byte has been received as part of an ongoing opaque sequence. - fn opaque_put(&mut self, _byte: u8) {} + /// We've reached the end of the ongoing APC sequence. + fn apc_end(&mut self) {} } #[cfg(all(test, feature = "no_std"))] @@ -499,9 +545,15 @@ mod tests { DcsHook(Vec>, Vec, bool, char), DcsPut(u8), DcsUnhook, - OpaqueStart(OpaqueSequenceKind), - OpaquePut(u8), - OpaqueEnd, + SosStart, + SosPut(u8), + SosEnd, + PmStart, + PmPut(u8), + PmEnd, + ApcStart, + ApcPut(u8), + ApcEnd, } impl Perform for Dispatcher { @@ -535,16 +587,40 @@ mod tests { self.dispatched.push(Sequence::DcsUnhook); } - fn opaque_start(&mut self, _kind: OpaqueSequenceKind) { - self.dispatched.push(Sequence::OpaqueStart(_kind)); + fn sos_start(&mut self) { + self.dispatched.push(Sequence::SosStart); + } + + fn sos_put(&mut self, byte: u8) { + self.dispatched.push(Sequence::SosPut(byte)); + } + + fn sos_end(&mut self) { + self.dispatched.push(Sequence::SosEnd); + } + + fn pm_start(&mut self) { + self.dispatched.push(Sequence::PmStart); + } + + fn pm_put(&mut self, byte: u8) { + self.dispatched.push(Sequence::PmPut(byte)); + } + + fn pm_end(&mut self) { + self.dispatched.push(Sequence::PmEnd); + } + + fn apc_start(&mut self) { + self.dispatched.push(Sequence::ApcStart); } - fn opaque_put(&mut self, byte: u8) { - self.dispatched.push(Sequence::OpaquePut(byte)); + fn apc_put(&mut self, byte: u8) { + self.dispatched.push(Sequence::ApcPut(byte)); } - fn opaque_end(&mut self) { - self.dispatched.push(Sequence::OpaqueEnd); + fn apc_end(&mut self) { + self.dispatched.push(Sequence::ApcEnd); } } @@ -563,7 +639,7 @@ mod tests { assert_eq!(params.len(), 2); assert_eq!(params[0], &OSC_BYTES[2..3]); assert_eq!(params[1], &OSC_BYTES[4..(OSC_BYTES.len() - 1)]); - }, + } _ => panic!("expected osc sequence"), } } @@ -600,7 +676,7 @@ mod tests { Sequence::Osc(params, _) => { assert_eq!(params.len(), MAX_OSC_PARAMS); assert!(params.iter().all(Vec::is_empty)); - }, + } _ => panic!("expected osc sequence"), } } @@ -658,7 +734,7 @@ mod tests { Sequence::Osc(params, _) => { assert_eq!(params[0], &[b'2']); assert_eq!(params[1], &INPUT[5..(INPUT.len() - 1)]); - }, + } _ => panic!("expected osc sequence"), } } @@ -677,11 +753,55 @@ mod tests { match &dispatcher.dispatched[0] { Sequence::Osc(params, _) => { assert_eq!(params[1], &INPUT[4..(INPUT.len() - 2)]); - }, + } _ => panic!("expected osc sequence"), } } + #[test] + fn parse_sos() { + const INPUT: &[u8] = b"\x1bXabc\x1b\\"; + + // Test with ESC \ terminator. + + let mut dispatcher = Dispatcher::default(); + let mut parser = Parser::new(); + + for byte in INPUT { + parser.advance(&mut dispatcher, *byte); + } + assert_eq!(dispatcher.dispatched.len(), 6); + assert_eq!(dispatcher.dispatched[0..5], vec![ + Sequence::SosStart, + Sequence::SosPut(b'a'), + Sequence::SosPut(b'b'), + Sequence::SosPut(b'c'), + Sequence::SosEnd, + ]) + } + + #[test] + fn parse_pm() { + const INPUT: &[u8] = b"\x1b^abc\x1b\\"; + + // Test with ESC \ terminator. + + let mut dispatcher = Dispatcher::default(); + let mut parser = Parser::new(); + + for byte in INPUT { + parser.advance(&mut dispatcher, *byte); + } + assert_eq!(dispatcher.dispatched.len(), 6); + assert_eq!(dispatcher.dispatched[0..5], vec![ + Sequence::PmStart, + Sequence::PmPut(b'a'), + Sequence::PmPut(b'b'), + Sequence::PmPut(b'c'), + Sequence::PmEnd, + ]) + } + #[test] fn parse_apc() { const INPUT: &[u8] = b"\x1b_abc\x1b\\"; @@ -696,11 +816,11 @@ mod tests { } assert_eq!(dispatcher.dispatched.len(), 6); assert_eq!(dispatcher.dispatched[0..5], vec![ - Sequence::OpaqueStart(OpaqueSequenceKind::Apc), - Sequence::OpaquePut(b'a'), - Sequence::OpaquePut(b'b'), - Sequence::OpaquePut(b'c'), - Sequence::OpaqueEnd, + Sequence::ApcStart, + Sequence::ApcPut(b'a'), + Sequence::ApcPut(b'b'), + Sequence::ApcPut(b'c'), + Sequence::ApcEnd, ]) } @@ -739,7 +859,7 @@ mod tests { #[cfg(feature = "no_std")] assert_eq!(params[1].len(), MAX_OSC_RAW - params[0].len()); - }, + } _ => panic!("expected osc sequence"), } } @@ -764,7 +884,7 @@ mod tests { Sequence::Csi(params, _, ignore, _) => { assert_eq!(params.len(), params::MAX_PARAMS); assert!(!ignore); - }, + } _ => panic!("expected csi sequence"), } } @@ -789,7 +909,7 @@ mod tests { Sequence::Csi(params, _, ignore, _) => { assert_eq!(params.len(), params::MAX_PARAMS); assert!(ignore); - }, + } _ => panic!("expected csi sequence"), } } @@ -861,7 +981,7 @@ mod tests { assert_eq!(intermediates, &[b'?']); assert_eq!(params, &[[1049]]); assert!(!ignore); - }, + } _ => panic!("expected csi sequence"), } } @@ -882,7 +1002,7 @@ mod tests { assert_eq!(params, &[vec![38, 2, 255, 0, 255], vec![1]]); assert_eq!(intermediates, &[]); assert!(!ignore); - }, + } _ => panic!("expected csi sequence"), } } @@ -904,7 +1024,7 @@ mod tests { assert_eq!(params.len(), params::MAX_PARAMS); assert!(params.iter().all(|param| param == &[1])); assert!(ignore); - }, + } _ => panic!("expected dcs sequence"), } } @@ -925,7 +1045,7 @@ mod tests { assert_eq!(intermediates, &[b'$']); assert_eq!(params, &[[1]]); assert!(!ignore); - }, + } _ => panic!("expected dcs sequence"), } assert_eq!(dispatcher.dispatched[1], Sequence::DcsPut(b'x')); @@ -947,7 +1067,7 @@ mod tests { Sequence::DcsHook(params, _, _, c) => { assert_eq!(params, &[[0], [1]]); assert_eq!(c, &'|'); - }, + } _ => panic!("expected dcs sequence"), } for (i, byte) in b"17/ab".iter().enumerate() { @@ -989,7 +1109,7 @@ mod tests { assert_eq!(intermediates, &[b'(']); assert_eq!(*byte, b'A'); assert!(!ignore); - }, + } _ => panic!("expected esc sequence"), } } @@ -1011,7 +1131,7 @@ mod tests { assert_eq!(params, &[[0; 32]]); assert_eq!(c, &'x'); assert!(ignore); - }, + } _ => panic!("expected csi sequence"), } } @@ -1033,7 +1153,7 @@ mod tests { assert_eq!(intermediates, &[b'?']); assert_eq!(params, &[[1049]]); assert!(!ignore); - }, + } _ => panic!("expected csi sequence"), } } @@ -1073,7 +1193,7 @@ mod tests { for item in params[1].iter() { assert_eq!(*item, b'a'); } - }, + } _ => panic!("expected osc sequence"), } } @@ -1103,7 +1223,7 @@ mod tests { Sequence::Osc(params, false) => { assert_eq!(params[0], b"2"); assert_eq!(params[1], INPUT_MIDDLE); - }, + } _ => panic!("expected osc sequence"), } } From ada43b8bd15236a8abc19333d7e84c74878eb586 Mon Sep 17 00:00:00 2001 From: Boaz Yaniv Date: Thu, 17 Oct 2024 15:22:53 +0900 Subject: [PATCH 5/6] Added trailing commas required by nightly cargo fmt Nightly cargo fmt seems to have added new rules since the last commit, so the code (including code that I didn't touch in this PR) doesn't past `cargo fmt +nightly -- --check` anymore. --- src/lib.rs | 89 +++++++++++++++++++++++++++--------------------------- 1 file changed, 45 insertions(+), 44 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 2090f40..4a9b0f5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -171,7 +171,7 @@ impl Parser { Action::None => (), action => { self.perform_action(performer, action, $arg); - } + }, } }; } @@ -180,18 +180,18 @@ impl Parser { State::Anywhere => { // Just run the action self.perform_action(performer, action, byte); - } + }, state => { match self.state { State::DcsPassthrough => { self.perform_action(performer, Action::Unhook, byte); - } + }, State::OscString => { self.perform_action(performer, Action::OscEnd, byte); - } + }, State::OpaqueString => { self.perform_action(performer, Action::OpaqueEnd, byte); - } + }, _ => (), } @@ -200,22 +200,22 @@ impl Parser { match state { State::CsiEntry | State::DcsEntry | State::Escape => { self.perform_action(performer, Action::Clear, byte); - } + }, State::DcsPassthrough => { self.perform_action(performer, Action::Hook, byte); - } + }, State::OscString => { self.perform_action(performer, Action::OscStart, byte); - } + }, State::OpaqueString => { self.perform_action(performer, Action::OpaqueStart, byte); - } + }, _ => (), } // Assume the new state self.state = state; - } + }, } } @@ -252,12 +252,12 @@ impl Parser { } performer.hook(self.params(), self.intermediates(), self.ignoring, byte as char); - } + }, Action::Put => performer.put(byte), Action::OscStart => { self.osc_raw.clear(); self.osc_num_params = 0; - } + }, Action::OscPut => { #[cfg(feature = "no_std")] { @@ -278,21 +278,21 @@ impl Parser { // First param is special - 0 to current byte index 0 => { self.osc_params[param_idx] = (0, idx); - } + }, // All other params depend on previous indexing _ => { let prev = self.osc_params[param_idx - 1]; let begin = prev.1; self.osc_params[param_idx] = (begin, idx); - } + }, } self.osc_num_params += 1; } else { self.osc_raw.push(byte); } - } + }, Action::OscEnd => { let param_idx = self.osc_num_params; let idx = self.osc_raw.len(); @@ -305,7 +305,7 @@ impl Parser { 0 => { self.osc_params[param_idx] = (0, idx); self.osc_num_params += 1; - } + }, // All other params depend on previous indexing _ => { @@ -313,10 +313,10 @@ impl Parser { let begin = prev.1; self.osc_params[param_idx] = (begin, idx); self.osc_num_params += 1; - } + }, } self.osc_dispatch(performer, byte); - } + }, Action::Unhook => performer.unhook(), Action::CsiDispatch => { if self.params.is_full() { @@ -331,10 +331,10 @@ impl Parser { self.ignoring, byte as char, ); - } + }, Action::EscDispatch => { performer.esc_dispatch(self.intermediates(), self.ignoring, byte); - } + }, Action::Collect => { if self.intermediate_idx == MAX_INTERMEDIATES { self.ignoring = true; @@ -342,7 +342,7 @@ impl Parser { self.intermediates[self.intermediate_idx] = byte; self.intermediate_idx += 1; } - } + }, Action::Param => { if self.params.is_full() { self.ignoring = true; @@ -360,7 +360,7 @@ impl Parser { self.param = self.param.saturating_mul(10); self.param = self.param.saturating_add((byte - b'0') as u16); } - } + }, Action::Clear => { // Reset everything on ESC/CSI/DCS entry self.intermediate_idx = 0; @@ -368,7 +368,7 @@ impl Parser { self.param = 0; self.params.clear(); - } + }, Action::BeginUtf8 => self.process_utf8(performer, byte), Action::Ignore => (), Action::None => (), @@ -392,7 +392,7 @@ impl Parser { OpaqueSequenceKind::Pm => performer.pm_start(), OpaqueSequenceKind::Apc => performer.apc_start(), } - } + }, Action::OpaquePut => { match self.opaque_sequence_kind { Some(OpaqueSequenceKind::Sos) => performer.sos_put(byte), @@ -402,7 +402,7 @@ impl Parser { // that the opaque_sequence_kind is set to a Some(x) value. None => unreachable!("opaque sequence kind not set"), } - } + }, Action::OpaqueEnd => { match self.opaque_sequence_kind { Some(OpaqueSequenceKind::Sos) => performer.sos_end(), @@ -413,7 +413,7 @@ impl Parser { None => unreachable!("opaque sequence kind not set"), } self.opaque_sequence_kind = None; - } + }, } } } @@ -470,7 +470,8 @@ pub trait Perform { _intermediates: &[u8], _ignore: bool, _action: char, - ) {} + ) { + } /// The final character of an escape sequence has arrived. /// @@ -639,7 +640,7 @@ mod tests { assert_eq!(params.len(), 2); assert_eq!(params[0], &OSC_BYTES[2..3]); assert_eq!(params[1], &OSC_BYTES[4..(OSC_BYTES.len() - 1)]); - } + }, _ => panic!("expected osc sequence"), } } @@ -676,7 +677,7 @@ mod tests { Sequence::Osc(params, _) => { assert_eq!(params.len(), MAX_OSC_PARAMS); assert!(params.iter().all(Vec::is_empty)); - } + }, _ => panic!("expected osc sequence"), } } @@ -734,7 +735,7 @@ mod tests { Sequence::Osc(params, _) => { assert_eq!(params[0], &[b'2']); assert_eq!(params[1], &INPUT[5..(INPUT.len() - 1)]); - } + }, _ => panic!("expected osc sequence"), } } @@ -753,7 +754,7 @@ mod tests { match &dispatcher.dispatched[0] { Sequence::Osc(params, _) => { assert_eq!(params[1], &INPUT[4..(INPUT.len() - 2)]); - } + }, _ => panic!("expected osc sequence"), } } @@ -859,7 +860,7 @@ mod tests { #[cfg(feature = "no_std")] assert_eq!(params[1].len(), MAX_OSC_RAW - params[0].len()); - } + }, _ => panic!("expected osc sequence"), } } @@ -884,7 +885,7 @@ mod tests { Sequence::Csi(params, _, ignore, _) => { assert_eq!(params.len(), params::MAX_PARAMS); assert!(!ignore); - } + }, _ => panic!("expected csi sequence"), } } @@ -909,7 +910,7 @@ mod tests { Sequence::Csi(params, _, ignore, _) => { assert_eq!(params.len(), params::MAX_PARAMS); assert!(ignore); - } + }, _ => panic!("expected csi sequence"), } } @@ -981,7 +982,7 @@ mod tests { assert_eq!(intermediates, &[b'?']); assert_eq!(params, &[[1049]]); assert!(!ignore); - } + }, _ => panic!("expected csi sequence"), } } @@ -1002,7 +1003,7 @@ mod tests { assert_eq!(params, &[vec![38, 2, 255, 0, 255], vec![1]]); assert_eq!(intermediates, &[]); assert!(!ignore); - } + }, _ => panic!("expected csi sequence"), } } @@ -1024,7 +1025,7 @@ mod tests { assert_eq!(params.len(), params::MAX_PARAMS); assert!(params.iter().all(|param| param == &[1])); assert!(ignore); - } + }, _ => panic!("expected dcs sequence"), } } @@ -1045,7 +1046,7 @@ mod tests { assert_eq!(intermediates, &[b'$']); assert_eq!(params, &[[1]]); assert!(!ignore); - } + }, _ => panic!("expected dcs sequence"), } assert_eq!(dispatcher.dispatched[1], Sequence::DcsPut(b'x')); @@ -1067,7 +1068,7 @@ mod tests { Sequence::DcsHook(params, _, _, c) => { assert_eq!(params, &[[0], [1]]); assert_eq!(c, &'|'); - } + }, _ => panic!("expected dcs sequence"), } for (i, byte) in b"17/ab".iter().enumerate() { @@ -1109,7 +1110,7 @@ mod tests { assert_eq!(intermediates, &[b'(']); assert_eq!(*byte, b'A'); assert!(!ignore); - } + }, _ => panic!("expected esc sequence"), } } @@ -1131,7 +1132,7 @@ mod tests { assert_eq!(params, &[[0; 32]]); assert_eq!(c, &'x'); assert!(ignore); - } + }, _ => panic!("expected csi sequence"), } } @@ -1153,7 +1154,7 @@ mod tests { assert_eq!(intermediates, &[b'?']); assert_eq!(params, &[[1049]]); assert!(!ignore); - } + }, _ => panic!("expected csi sequence"), } } @@ -1193,7 +1194,7 @@ mod tests { for item in params[1].iter() { assert_eq!(*item, b'a'); } - } + }, _ => panic!("expected osc sequence"), } } @@ -1223,7 +1224,7 @@ mod tests { Sequence::Osc(params, false) => { assert_eq!(params[0], b"2"); assert_eq!(params[1], INPUT_MIDDLE); - } + }, _ => panic!("expected osc sequence"), } } From 4839d4dcbcadf05f2401d6723de270f5bfd71899 Mon Sep 17 00:00:00 2001 From: Boaz Yaniv Date: Mon, 21 Oct 2024 17:27:40 +0900 Subject: [PATCH 6/6] Combine two match clauses --- src/lib.rs | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 4a9b0f5..17d7b43 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -376,9 +376,18 @@ impl Parser { // APC Actions are checked last, since they are relatively rare. Action::OpaqueStart => { let kind = match byte { - 0x58 => OpaqueSequenceKind::Sos, - 0x5e => OpaqueSequenceKind::Pm, - 0x5f => OpaqueSequenceKind::Apc, + 0x58 => { + performer.sos_start(); + OpaqueSequenceKind::Sos + }, + 0x5e => { + performer.pm_start(); + OpaqueSequenceKind::Pm + }, + 0x5f => { + performer.apc_start(); + OpaqueSequenceKind::Apc + }, // Changes to OpaqueString state which trigger this action are only possible // when one of the escape sequences above is detected (see Escape state changes @@ -387,11 +396,6 @@ impl Parser { _ => unreachable!("invalid opaque sequence kind"), }; self.opaque_sequence_kind = Some(kind); - match kind { - OpaqueSequenceKind::Sos => performer.sos_start(), - OpaqueSequenceKind::Pm => performer.pm_start(), - OpaqueSequenceKind::Apc => performer.apc_start(), - } }, Action::OpaquePut => { match self.opaque_sequence_kind { @@ -961,7 +965,7 @@ mod tests { assert_eq!(dispatcher.dispatched.len(), 1); match &dispatcher.dispatched[0] { - Sequence::Csi(params, ..) => assert_eq!(params, &[[std::u16::MAX as u16]]), + Sequence::Csi(params, ..) => assert_eq!(params, &[[std::u16::MAX]]), _ => panic!("expected csi sequence"), } }