aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--examples/parselog.rs12
-rw-r--r--src/lib.rs128
-rw-r--r--src/params.rs142
-rw-r--r--src/table.rs12
4 files changed, 225 insertions, 69 deletions
diff --git a/examples/parselog.rs b/examples/parselog.rs
index c310182..dfd0aee 100644
--- a/examples/parselog.rs
+++ b/examples/parselog.rs
@@ -1,12 +1,12 @@
//! Parse input from stdin and log actions on stdout
use std::io::{self, Read};
-use vte;
+use vte::{Params, Parser, Perform};
/// A type implementing Perform that just logs actions
struct Log;
-impl vte::Perform for Log {
+impl Perform for Log {
fn print(&mut self, c: char) {
println!("[print] {:?}", c);
}
@@ -15,7 +15,7 @@ impl vte::Perform for Log {
println!("[execute] {:02x}", byte);
}
- fn hook(&mut self, params: &[i64], intermediates: &[u8], ignore: bool, c: char) {
+ fn hook(&mut self, params: &Params, intermediates: &[u8], ignore: bool, c: char) {
println!(
"[hook] params={:?}, intermediates={:?}, ignore={:?}, char={:?}",
params, intermediates, ignore, c
@@ -34,9 +34,9 @@ impl vte::Perform for Log {
println!("[osc_dispatch] params={:?} bell_terminated={}", params, bell_terminated);
}
- fn csi_dispatch(&mut self, params: &[i64], intermediates: &[u8], ignore: bool, c: char) {
+ fn csi_dispatch(&mut self, params: &Params, intermediates: &[u8], ignore: bool, c: char) {
println!(
- "[csi_dispatch] params={:?}, intermediates={:?}, ignore={:?}, char={:?}",
+ "[csi_dispatch] params={:#?}, intermediates={:?}, ignore={:?}, char={:?}",
params, intermediates, ignore, c
);
}
@@ -53,7 +53,7 @@ fn main() {
let input = io::stdin();
let mut handle = input.lock();
- let mut statemachine = vte::Parser::new();
+ let mut statemachine = Parser::new();
let mut performer = Log;
let mut buf = [0; 2048];
diff --git a/src/lib.rs b/src/lib.rs
index a99638a..749dd21 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -41,14 +41,17 @@ use arrayvec::ArrayVec;
use utf8parse as utf8;
mod definitions;
+mod params;
mod table;
+pub use params::{Params, ParamsIter};
+
use definitions::{unpack, Action, State};
const MAX_INTERMEDIATES: usize = 2;
+const MAX_OSC_PARAMS: usize = 16;
#[cfg(any(feature = "no_std", test))]
const MAX_OSC_RAW: usize = 1024;
-const MAX_PARAMS: usize = 16;
struct VtUtf8Receiver<'a, P: Perform>(&'a mut P, &'a mut State);
@@ -72,14 +75,13 @@ pub struct Parser {
state: State,
intermediates: [u8; MAX_INTERMEDIATES],
intermediate_idx: usize,
- params: [i64; MAX_PARAMS],
+ params: Params,
param: i64,
- num_params: usize,
#[cfg(feature = "no_std")]
osc_raw: ArrayVec<[u8; MAX_OSC_RAW]>,
#[cfg(not(feature = "no_std"))]
osc_raw: Vec<u8>,
- osc_params: [(usize, usize); MAX_PARAMS],
+ osc_params: [(usize, usize); MAX_OSC_PARAMS],
osc_num_params: usize,
ignoring: bool,
utf8_parser: utf8::Parser,
@@ -92,8 +94,8 @@ impl Parser {
}
#[inline]
- fn params(&self) -> &[i64] {
- &self.params[..self.num_params]
+ fn params(&self) -> &Params {
+ &self.params
}
#[inline]
@@ -198,7 +200,7 @@ impl Parser {
/// The aliasing is needed here for multiple slices into self.osc_raw
#[inline]
fn osc_dispatch<P: Perform>(&self, performer: &mut P, byte: u8) {
- let mut slices: [MaybeUninit<&[u8]>; MAX_PARAMS] =
+ let mut slices: [MaybeUninit<&[u8]>; MAX_OSC_PARAMS] =
unsafe { MaybeUninit::uninit().assume_init() };
for (i, slice) in slices.iter_mut().enumerate().take(self.osc_num_params) {
@@ -219,11 +221,10 @@ impl Parser {
Action::Print => performer.print(byte as char),
Action::Execute => performer.execute(byte),
Action::Hook => {
- if self.num_params == MAX_PARAMS {
+ if self.params.is_full() {
self.ignoring = true;
} else {
- self.params[self.num_params] = self.param;
- self.num_params += 1;
+ self.params.push(self.param);
}
performer.hook(self.params(), self.intermediates(), self.ignoring, byte as char);
@@ -247,8 +248,8 @@ impl Parser {
if byte == b';' {
let param_idx = self.osc_num_params;
match param_idx {
- // Only process up to MAX_PARAMS
- MAX_PARAMS => return,
+ // Only process up to MAX_OSC_PARAMS
+ MAX_OSC_PARAMS => return,
// First param is special - 0 to current byte index
0 => {
@@ -274,7 +275,7 @@ impl Parser {
match param_idx {
// Finish last parameter if not already maxed
- MAX_PARAMS => (),
+ MAX_OSC_PARAMS => (),
// First param is special - 0 to current byte index
0 => {
@@ -294,11 +295,10 @@ impl Parser {
},
Action::Unhook => performer.unhook(),
Action::CsiDispatch => {
- if self.num_params == MAX_PARAMS {
+ if self.params.is_full() {
self.ignoring = true;
} else {
- self.params[self.num_params] = self.param;
- self.num_params += 1;
+ self.params.push(self.param);
}
performer.csi_dispatch(
@@ -320,18 +320,17 @@ impl Parser {
}
},
Action::Param => {
- // Completed a param
- let idx = self.num_params;
-
- if idx == MAX_PARAMS {
+ if self.params.is_full() {
self.ignoring = true;
return;
}
if byte == b';' {
- self.params[idx] = self.param;
+ self.params.push(self.param);
+ self.param = 0;
+ } else if byte == b':' {
+ self.params.extend(self.param);
self.param = 0;
- self.num_params += 1;
} else {
// Continue collecting bytes into param
self.param = self.param.saturating_mul(10);
@@ -342,8 +341,9 @@ impl Parser {
// Reset everything on ESC/CSI/DCS entry
self.intermediate_idx = 0;
self.ignoring = false;
- self.num_params = 0;
self.param = 0;
+
+ self.params.clear();
},
Action::BeginUtf8 => self.process_utf8(performer, byte),
Action::Ignore => (),
@@ -378,7 +378,7 @@ pub trait Perform {
///
/// The `ignore` flag indicates that more than two intermediates arrived and
/// subsequent characters were ignored.
- fn hook(&mut self, params: &[i64], intermediates: &[u8], ignore: bool, action: char);
+ fn hook(&mut self, params: &Params, intermediates: &[u8], ignore: bool, action: char);
/// Pass bytes as part of a device control string to the handle chosen in `hook`. C0 controls
/// will also be passed to the handler.
@@ -398,7 +398,7 @@ pub trait Perform {
/// The `ignore` flag indicates that either more than two intermediates arrived
/// or the number of parameters exceeded the maximum supported length,
/// and subsequent characters were ignored.
- fn csi_dispatch(&mut self, params: &[i64], intermediates: &[u8], ignore: bool, action: char);
+ fn csi_dispatch(&mut self, params: &Params, intermediates: &[u8], ignore: bool, action: char);
/// The final character of an escape sequence has arrived.
///
@@ -439,7 +439,7 @@ mod tests {
fn execute(&mut self, _: u8) {}
- fn hook(&mut self, _: &[i64], _: &[u8], _: bool, _: char) {}
+ fn hook(&mut self, _: &Params, _: &[u8], _: bool, _: char) {}
fn put(&mut self, _: u8) {}
@@ -452,7 +452,7 @@ mod tests {
self.params = params.iter().map(|p| p.to_vec()).collect();
}
- fn csi_dispatch(&mut self, _: &[i64], _: &[u8], _: bool, _: char) {}
+ fn csi_dispatch(&mut self, _: &Params, _: &[u8], _: bool, _: char) {}
fn esc_dispatch(&mut self, _: &[u8], _: bool, _: u8) {}
}
@@ -461,7 +461,7 @@ mod tests {
struct CsiDispatcher {
dispatched_csi: bool,
ignore: bool,
- params: Vec<i64>,
+ params: Vec<Vec<i64>>,
intermediates: Vec<u8>,
}
@@ -470,7 +470,7 @@ mod tests {
fn execute(&mut self, _: u8) {}
- fn hook(&mut self, _: &[i64], _: &[u8], _: bool, _: char) {}
+ fn hook(&mut self, _: &Params, _: &[u8], _: bool, _: char) {}
fn put(&mut self, _: u8) {}
@@ -478,11 +478,11 @@ mod tests {
fn osc_dispatch(&mut self, _: &[&[u8]], _: bool) {}
- fn csi_dispatch(&mut self, params: &[i64], intermediates: &[u8], ignore: bool, _: char) {
+ fn csi_dispatch(&mut self, params: &Params, intermediates: &[u8], ignore: bool, _: char) {
self.intermediates = intermediates.to_vec();
- self.params = params.to_vec();
- self.ignore = ignore;
self.dispatched_csi = true;
+ self.params = params.iter().map(|subparam| subparam.to_vec()).collect();
+ self.ignore = ignore;
}
fn esc_dispatch(&mut self, _: &[u8], _: bool, _: u8) {}
@@ -503,9 +503,9 @@ mod tests {
fn execute(&mut self, _: u8) {}
- fn hook(&mut self, params: &[i64], intermediates: &[u8], ignore: bool, c: char) {
+ fn hook(&mut self, params: &Params, intermediates: &[u8], ignore: bool, c: char) {
self.intermediates = intermediates.to_vec();
- self.params = params.to_vec();
+ self.params = params.iter().map(|x| x.to_vec()).flatten().collect();
self.ignore = ignore;
self.c = Some(c);
self.dispatched_dcs = true;
@@ -521,7 +521,7 @@ mod tests {
fn osc_dispatch(&mut self, _: &[&[u8]], _: bool) {}
- fn csi_dispatch(&mut self, _: &[i64], _: &[u8], _: bool, _: char) {}
+ fn csi_dispatch(&mut self, _: &Params, _: &[u8], _: bool, _: char) {}
fn esc_dispatch(&mut self, _: &[u8], _: bool, _: u8) {}
}
@@ -539,7 +539,7 @@ mod tests {
fn execute(&mut self, _: u8) {}
- fn hook(&mut self, _: &[i64], _: &[u8], _: bool, _: char) {}
+ fn hook(&mut self, _: &Params, _: &[u8], _: bool, _: char) {}
fn put(&mut self, _: u8) {}
@@ -547,7 +547,7 @@ mod tests {
fn osc_dispatch(&mut self, _: &[&[u8]], _: bool) {}
- fn csi_dispatch(&mut self, _: &[i64], _: &[u8], _: bool, _: char) {}
+ fn csi_dispatch(&mut self, _: &Params, _: &[u8], _: bool, _: char) {}
fn esc_dispatch(&mut self, intermediates: &[u8], ignore: bool, byte: u8) {
self.intermediates = intermediates.to_vec();
@@ -588,17 +588,18 @@ mod tests {
#[test]
fn parse_osc_max_params() {
- static INPUT: &[u8] = b"\x1b];;;;;;;;;;;;;;;;;\x1b";
+ let params = std::iter::repeat(";").take(params::MAX_PARAMS + 1).collect::<String>();
+ let input = format!("\x1b]{}\x1b", &params[..]).into_bytes();
let mut dispatcher = OscDispatcher::default();
let mut parser = Parser::new();
- for byte in INPUT {
- parser.advance(&mut dispatcher, *byte);
+ for byte in input {
+ parser.advance(&mut dispatcher, byte);
}
// Check that flag is set and thus osc_dispatch assertions ran.
assert!(dispatcher.dispatched_osc);
- assert_eq!(dispatcher.params.len(), MAX_PARAMS);
+ assert_eq!(dispatcher.params.len(), MAX_OSC_PARAMS);
for param in dispatcher.params.iter() {
assert_eq!(param.len(), 0);
}
@@ -606,18 +607,19 @@ mod tests {
#[test]
fn parse_dcs_max_params() {
- static INPUT: &[u8] = b"\x1bP1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;p\x1b";
+ let params = std::iter::repeat("1;").take(params::MAX_PARAMS + 1).collect::<String>();
+ let input = format!("\x1bP{}p", &params[..]).into_bytes();
let mut dispatcher = DcsDispatcher::default();
let mut parser = Parser::new();
- for byte in INPUT {
- parser.advance(&mut dispatcher, *byte);
+ for byte in input {
+ parser.advance(&mut dispatcher, byte);
}
// Check that flag is set and thus osc_dispatch assertions ran.
assert!(dispatcher.ignore);
assert!(dispatcher.dispatched_dcs);
- assert_eq!(dispatcher.params.len(), MAX_PARAMS);
+ assert_eq!(dispatcher.params.len(), params::MAX_PARAMS);
for param in dispatcher.params.iter() {
assert_eq!(*param, 1);
}
@@ -656,7 +658,7 @@ mod tests {
// This will build a list of repeating '1;'s
// The length is MAX_PARAMS - 1 because the last semicolon is interpreted
// as an implicit zero, making the total number of parameters MAX_PARAMS
- let params = std::iter::repeat("1;").take(MAX_PARAMS - 1).collect::<String>();
+ let params = std::iter::repeat("1;").take(params::MAX_PARAMS - 1).collect::<String>();
let input = format!("\x1b[{}p", &params[..]).into_bytes();
let mut dispatcher = CsiDispatcher::default();
@@ -668,7 +670,7 @@ mod tests {
// Check that flag is set and thus csi_dispatch assertions ran.
assert!(dispatcher.dispatched_csi);
- assert_eq!(dispatcher.params.len(), MAX_PARAMS);
+ assert_eq!(dispatcher.params.len(), params::MAX_PARAMS);
assert!(!dispatcher.ignore);
}
@@ -677,7 +679,7 @@ mod tests {
// This will build a list of repeating '1;'s
// The length is MAX_PARAMS because the last semicolon is interpreted
// as an implicit zero, making the total number of parameters MAX_PARAMS + 1
- let params = std::iter::repeat("1;").take(MAX_PARAMS).collect::<String>();
+ let params = std::iter::repeat("1;").take(params::MAX_PARAMS).collect::<String>();
let input = format!("\x1b[{}p", &params[..]).into_bytes();
let mut dispatcher = CsiDispatcher::default();
@@ -689,7 +691,7 @@ mod tests {
// Check that flag is set and thus csi_dispatch assertions ran.
assert!(dispatcher.dispatched_csi);
- assert_eq!(dispatcher.params.len(), MAX_PARAMS);
+ assert_eq!(dispatcher.params.len(), params::MAX_PARAMS);
assert!(dispatcher.ignore);
}
@@ -702,7 +704,7 @@ mod tests {
parser.advance(&mut dispatcher, *byte);
}
- assert_eq!(dispatcher.params, &[4, 0]);
+ assert_eq!(dispatcher.params, &[[4], [0]]);
}
#[test]
@@ -716,7 +718,7 @@ mod tests {
}
// Check that flag is set and thus osc_dispatch assertions ran.
- assert_eq!(dispatcher.params, &[0, 4]);
+ assert_eq!(dispatcher.params, &[[0], [4]]);
}
#[test]
@@ -730,7 +732,7 @@ mod tests {
parser.advance(&mut dispatcher, *byte);
}
- assert_eq!(dispatcher.params, &[i64::MAX as i64]);
+ assert_eq!(dispatcher.params, &[[i64::MAX as i64]]);
}
#[test]
@@ -746,7 +748,23 @@ mod tests {
assert!(dispatcher.dispatched_csi);
assert!(!dispatcher.ignore);
assert_eq!(dispatcher.intermediates, &[b'?']);
- assert_eq!(dispatcher.params, &[1049]);
+ assert_eq!(dispatcher.params, &[[1049]]);
+ }
+
+ #[test]
+ fn csi_subparameters() {
+ static INPUT: &[u8] = b"\x1b[38:2:255:0:255;1m";
+ let mut dispatcher = CsiDispatcher::default();
+ let mut parser = Parser::new();
+
+ for byte in INPUT {
+ parser.advance(&mut dispatcher, *byte);
+ }
+
+ assert!(dispatcher.dispatched_csi);
+ assert!(!dispatcher.ignore);
+ assert_eq!(dispatcher.intermediates, &[]);
+ assert_eq!(dispatcher.params, &[vec![38, 2, 255, 0, 255], vec![1]]);
}
#[test]
@@ -887,7 +905,7 @@ mod bench {
black_box(byte);
}
- fn hook(&mut self, params: &[i64], intermediates: &[u8], ignore: bool, c: char) {
+ fn hook(&mut self, params: &Params, intermediates: &[u8], ignore: bool, c: char) {
black_box((params, intermediates, ignore, c));
}
@@ -901,7 +919,7 @@ mod bench {
black_box((params, bell_terminated));
}
- fn csi_dispatch(&mut self, params: &[i64], intermediates: &[u8], ignore: bool, c: char) {
+ fn csi_dispatch(&mut self, params: &Params, intermediates: &[u8], ignore: bool, c: char) {
black_box((params, intermediates, ignore, c));
}
diff --git a/src/params.rs b/src/params.rs
new file mode 100644
index 0000000..efa24cb
--- /dev/null
+++ b/src/params.rs
@@ -0,0 +1,142 @@
+//! Fixed size parameters list with optional subparameters.
+
+use core::fmt::{self, Debug, Formatter};
+
+pub(crate) const MAX_PARAMS: usize = 32;
+
+#[derive(Default)]
+pub struct Params {
+ /// Number of subparameters for each parameter.
+ ///
+ /// For each entry in the `params` slice, this stores the length of the param as number of
+ /// subparams at the same index as the param in the `params` slice.
+ ///
+ /// At the subparam positions the length will always be `0`.
+ subparams: [u8; MAX_PARAMS],
+
+ /// All parameters and subparameters.
+ params: [i64; MAX_PARAMS],
+
+ /// Number of suparameters in the current parameter.
+ current_subparams: u8,
+
+ /// Total number of parameters and subparameters.
+ len: usize,
+}
+
+impl Params {
+ /// Returns the number of parameters.
+ #[inline]
+ pub fn len(&self) -> usize {
+ self.len
+ }
+
+ /// Returns `true` if there are no parameters present.
+ #[inline]
+ pub fn is_empty(&self) -> bool {
+ self.len == 0
+ }
+
+ /// Returns an iterator over all parameters and subparameters.
+ #[inline]
+ pub fn iter(&self) -> ParamsIter<'_> {
+ ParamsIter::new(self)
+ }
+
+ /// Returns `true` if there is no more space for additional parameters.
+ #[inline]
+ pub(crate) fn is_full(&self) -> bool {
+ self.len == MAX_PARAMS
+ }
+
+ /// Clear all parameters.
+ #[inline]
+ pub(crate) fn clear(&mut self) {
+ self.current_subparams = 0;
+ self.len = 0;
+ }
+
+ /// Add an additional parameter.
+ #[inline]
+ pub(crate) fn push(&mut self, item: i64) {
+ self.subparams[self.len - self.current_subparams as usize] = self.current_subparams + 1;
+ self.params[self.len] = item;
+ self.current_subparams = 0;
+ self.len += 1;
+ }
+
+ /// Add an additional subparameter to the current parameter.
+ #[inline]
+ pub(crate) fn extend(&mut self, item: i64) {
+ self.params[self.len] = item;
+ self.current_subparams += 1;
+ self.len += 1;
+ }
+}
+
+impl<'a> IntoIterator for &'a Params {
+ type IntoIter = ParamsIter<'a>;
+ type Item = &'a [i64];
+
+ fn into_iter(self) -> Self::IntoIter {
+ self.iter()
+ }
+}
+
+/// Immutable subparameter iterator.
+pub struct ParamsIter<'a> {
+ params: &'a Params,
+ index: usize,
+}
+
+impl<'a> ParamsIter<'a> {
+ fn new(params: &'a Params) -> Self {
+ Self { params, index: 0 }
+ }
+}
+
+impl<'a> Iterator for ParamsIter<'a> {
+ type Item = &'a [i64];
+
+ fn next(&mut self) -> Option<Self::Item> {
+ if self.index >= self.params.len() {
+ return None;
+ }
+
+ // Get all subparameters for the current parameter.
+ let num_subparams = self.params.subparams[self.index];
+ let param = &self.params.params[self.index..self.index + num_subparams as usize];
+
+ // Jump to the next parameter.
+ self.index += num_subparams as usize;
+
+ Some(param)
+ }
+
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ let remaining = self.params.len() - self.index;
+ (remaining, Some(remaining))
+ }
+}
+
+impl Debug for Params {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ write!(f, "[")?;
+
+ for (i, param) in self.iter().enumerate() {
+ if i != 0 {
+ write!(f, ";")?;
+ }
+
+ for (i, subparam) in param.iter().enumerate() {
+ if i != 0 {
+ write!(f, ":")?;
+ }
+
+ subparam.fmt(f)?;
+ }
+ }
+
+ write!(f, "]")
+ }
+}
diff --git a/src/table.rs b/src/table.rs
index c19e4ab..f2c0105 100644
--- a/src/table.rs
+++ b/src/table.rs
@@ -64,9 +64,8 @@ generate_state_changes!(state_changes, {
0x1c..=0x1f => (Anywhere, Execute),
0x7f => (Anywhere, Ignore),
0x20..=0x2f => (CsiIntermediate, Collect),
- 0x3a => (CsiIgnore, None),
0x30..=0x39 => (CsiParam, Param),
- 0x3b => (CsiParam, Param),
+ 0x3a..=0x3b => (CsiParam, Param),
0x3c..=0x3f => (CsiParam, Collect),
0x40..=0x7e => (Ground, CsiDispatch),
},
@@ -85,9 +84,8 @@ generate_state_changes!(state_changes, {
0x19 => (Anywhere, Execute),
0x1c..=0x1f => (Anywhere, Execute),
0x30..=0x39 => (Anywhere, Param),
- 0x3b => (Anywhere, Param),
+ 0x3a..=0x3b => (Anywhere, Param),
0x7f => (Anywhere, Ignore),
- 0x3a => (CsiIgnore, None),
0x3c..=0x3f => (CsiIgnore, None),
0x20..=0x2f => (CsiIntermediate, Collect),
0x40..=0x7e => (Ground, CsiDispatch),
@@ -108,10 +106,9 @@ generate_state_changes!(state_changes, {
0x19 => (Anywhere, Ignore),
0x1c..=0x1f => (Anywhere, Ignore),
0x7f => (Anywhere, Ignore),
- 0x3a => (DcsIgnore, None),
0x20..=0x2f => (DcsIntermediate, Collect),
0x30..=0x39 => (DcsParam, Param),
- 0x3b => (DcsParam, Param),
+ 0x3a..=0x3b => (DcsParam, Param),
0x3c..=0x3f => (DcsParam, Collect),
0x40..=0x7e => (DcsPassthrough, None),
},
@@ -139,9 +136,8 @@ generate_state_changes!(state_changes, {
0x19 => (Anywhere, Ignore),
0x1c..=0x1f => (Anywhere, Ignore),
0x30..=0x39 => (Anywhere, Param),
- 0x3b => (Anywhere, Param),
+ 0x3a..=0x3b => (Anywhere, Param),
0x7f => (Anywhere, Ignore),
- 0x3a => (DcsIgnore, None),
0x3c..=0x3f => (DcsIgnore, None),
0x20..=0x2f => (DcsIntermediate, Collect),
0x40..=0x7e => (DcsPassthrough, None),