-
Notifications
You must be signed in to change notification settings - Fork 57
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added APC support #110
Added APC support #110
Changes from all commits
1af8b6e
955d293
b6b8e50
946346a
e3efc4e
cc99a0f
e2c61e2
642e207
8e10a22
bb85177
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -89,6 +89,7 @@ pub struct Parser<const OSC_RAW_BUF_SIZE: usize = MAX_OSC_RAW> { | |
osc_num_params: usize, | ||
ignoring: bool, | ||
utf8_parser: utf8::Parser, | ||
str_start: u8, | ||
} | ||
|
||
impl Parser { | ||
|
@@ -185,7 +186,7 @@ impl<const OSC_RAW_BUF_SIZE: usize> Parser<OSC_RAW_BUF_SIZE> { | |
self.perform_action(performer, Action::Unhook, byte); | ||
}, | ||
State::OscString => { | ||
self.perform_action(performer, Action::OscEnd, byte); | ||
self.perform_action(performer, Action::StringEnd, byte); | ||
}, | ||
_ => (), | ||
} | ||
|
@@ -200,7 +201,7 @@ impl<const OSC_RAW_BUF_SIZE: usize> Parser<OSC_RAW_BUF_SIZE> { | |
self.perform_action(performer, Action::Hook, byte); | ||
}, | ||
State::OscString => { | ||
self.perform_action(performer, Action::OscStart, byte); | ||
self.perform_action(performer, Action::StringStart, byte); | ||
}, | ||
_ => (), | ||
} | ||
|
@@ -231,6 +232,21 @@ impl<const OSC_RAW_BUF_SIZE: usize> Parser<OSC_RAW_BUF_SIZE> { | |
} | ||
} | ||
|
||
#[inline] | ||
fn apc_dispatch<P: Perform>(&self, performer: &mut P) { | ||
performer.apc_dispatch(&self.osc_raw); | ||
} | ||
|
||
#[inline] | ||
fn pm_dispatch<P: Perform>(&self, performer: &mut P) { | ||
performer.pm_dispatch(&self.osc_raw); | ||
} | ||
|
||
#[inline] | ||
fn sos_dispatch<P: Perform>(&self, performer: &mut P) { | ||
performer.sos_dispatch(&self.osc_raw); | ||
} | ||
|
||
#[inline] | ||
fn perform_action<P: Perform>(&mut self, performer: &mut P, action: Action, byte: u8) { | ||
match action { | ||
|
@@ -246,11 +262,12 @@ impl<const OSC_RAW_BUF_SIZE: usize> Parser<OSC_RAW_BUF_SIZE> { | |
performer.hook(self.params(), self.intermediates(), self.ignoring, byte as char); | ||
}, | ||
Action::Put => performer.put(byte), | ||
Action::OscStart => { | ||
Action::StringStart => { | ||
self.str_start = byte % 64; | ||
self.osc_raw.clear(); | ||
self.osc_num_params = 0; | ||
}, | ||
Action::OscPut => { | ||
Action::StringPut => { | ||
#[cfg(feature = "no_std")] | ||
{ | ||
if self.osc_raw.is_full() { | ||
|
@@ -261,7 +278,7 @@ impl<const OSC_RAW_BUF_SIZE: usize> Parser<OSC_RAW_BUF_SIZE> { | |
let idx = self.osc_raw.len(); | ||
|
||
// Param separator | ||
if byte == b';' { | ||
if self.str_start == 0x1d && byte == b';' { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One extra branch isn't going to be the end of the world, but I don't see how this isn't just going to be a net-negative for anyone who doesn't use APC escapes (which is basically everyone). |
||
let param_idx = self.osc_num_params; | ||
match param_idx { | ||
// Only process up to MAX_OSC_PARAMS | ||
|
@@ -285,29 +302,45 @@ impl<const OSC_RAW_BUF_SIZE: usize> Parser<OSC_RAW_BUF_SIZE> { | |
self.osc_raw.push(byte); | ||
} | ||
}, | ||
Action::OscEnd => { | ||
Action::StringEnd => { | ||
let param_idx = self.osc_num_params; | ||
let idx = self.osc_raw.len(); | ||
|
||
match param_idx { | ||
// Finish last parameter if not already maxed | ||
MAX_OSC_PARAMS => (), | ||
|
||
// First param is special - 0 to current byte index | ||
0 => { | ||
self.osc_params[param_idx] = (0, idx); | ||
self.osc_num_params += 1; | ||
match self.str_start { | ||
0x1d => { | ||
match param_idx { | ||
// Finish last parameter if not already maxed | ||
MAX_OSC_PARAMS => (), | ||
|
||
// First param is special - 0 to current byte index | ||
0 => { | ||
self.osc_params[param_idx] = (0, idx); | ||
self.osc_num_params += 1; | ||
}, | ||
|
||
// 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; | ||
}, | ||
} | ||
self.osc_dispatch(performer, byte); | ||
}, | ||
|
||
// 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; | ||
0x18 => { | ||
self.sos_dispatch(performer); | ||
}, | ||
0x1e => { | ||
self.pm_dispatch(performer); | ||
}, | ||
0x1F => { | ||
self.apc_dispatch(performer); | ||
}, | ||
// This condition should never be hit under the | ||
// current way in which the code is ran. | ||
_ => unreachable!(), | ||
Comment on lines
+340
to
+342
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This comment is basically just a long way to say |
||
} | ||
Comment on lines
+309
to
343
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This basically feels like an embedded parser. Most of our parser is structured in a way to stream through bytes and process them as they're coming in, however rather than creating state transitions you're storing a state transition to later then match on it instead. VTE is already too slow and I'm afraid this will just amplify that problem. |
||
self.osc_dispatch(performer, byte); | ||
}, | ||
Action::Unhook => performer.unhook(), | ||
Action::CsiDispatch => { | ||
|
@@ -428,6 +461,18 @@ 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) {} | ||
|
||
/// Called at the end of an APC (application program command), where | ||
/// `bytes` is the content of the APC. | ||
fn apc_dispatch(&mut self, _bytes: &[u8]) {} | ||
|
||
/// Called at the end of an SOS (start of string), where | ||
/// `bytes` is the content of the SOS. | ||
fn sos_dispatch(&mut self, _bytes: &[u8]) {} | ||
|
||
/// Called at the end of a PM (privacy message), where | ||
/// `bytes` is the content of the PM. | ||
fn pm_dispatch(&mut self, _bytes: &[u8]) {} | ||
} | ||
|
||
#[cfg(all(test, feature = "no_std"))] | ||
|
@@ -460,6 +505,9 @@ mod tests { | |
DcsHook(Vec<Vec<u16>>, Vec<u8>, bool, char), | ||
DcsPut(u8), | ||
DcsUnhook, | ||
Apc(Vec<u8>), | ||
Pm(Vec<u8>), | ||
Sos(Vec<u8>), | ||
} | ||
|
||
impl Perform for Dispatcher { | ||
|
@@ -492,6 +540,18 @@ mod tests { | |
fn unhook(&mut self) { | ||
self.dispatched.push(Sequence::DcsUnhook); | ||
} | ||
|
||
fn apc_dispatch(&mut self, bytes: &[u8]) { | ||
self.dispatched.push(Sequence::Apc(bytes.to_vec())) | ||
} | ||
|
||
fn sos_dispatch(&mut self, bytes: &[u8]) { | ||
self.dispatched.push(Sequence::Sos(bytes.to_vec())) | ||
} | ||
|
||
fn pm_dispatch(&mut self, bytes: &[u8]) { | ||
self.dispatched.push(Sequence::Pm(bytes.to_vec())) | ||
} | ||
} | ||
|
||
#[test] | ||
|
@@ -880,6 +940,59 @@ mod tests { | |
assert_eq!(dispatcher.dispatched[6], Sequence::DcsUnhook); | ||
} | ||
|
||
#[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, | ||
vec![Sequence::Apc(b"abc".to_vec()), Sequence::Esc(vec![], false, 92)] | ||
) | ||
} | ||
#[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, | ||
vec![Sequence::Pm(b"abc".to_vec()), Sequence::Esc(vec![], false, 92)] | ||
) | ||
} | ||
|
||
#[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, | ||
vec![Sequence::Sos(b"abc".to_vec()), Sequence::Esc(vec![], false, 92)] | ||
) | ||
} | ||
|
||
#[test] | ||
fn intermediate_reset_on_dcs_exit() { | ||
static INPUT: &[u8] = b"\x1bP=1sZZZ\x1b+\x5c"; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is just a bad name. A "null state" already exists, it's called
Ground
. Looking atState::Null
alone would make it impossible to tell what that state is for.