170 lines
5.2 KiB
Rust
170 lines
5.2 KiB
Rust
|
use criterion::{black_box, Criterion};
|
||
|
|
||
|
use anstyle_parse::*;
|
||
|
|
||
|
struct BenchDispatcher;
|
||
|
impl Perform for BenchDispatcher {
|
||
|
fn print(&mut self, c: char) {
|
||
|
black_box(c);
|
||
|
}
|
||
|
|
||
|
fn execute(&mut self, byte: u8) {
|
||
|
black_box(byte);
|
||
|
}
|
||
|
|
||
|
fn hook(&mut self, params: &Params, intermediates: &[u8], ignore: bool, c: u8) {
|
||
|
black_box((params, intermediates, ignore, c));
|
||
|
}
|
||
|
|
||
|
fn put(&mut self, byte: u8) {
|
||
|
black_box(byte);
|
||
|
}
|
||
|
|
||
|
fn osc_dispatch(&mut self, params: &[&[u8]], bell_terminated: bool) {
|
||
|
black_box((params, bell_terminated));
|
||
|
}
|
||
|
|
||
|
fn csi_dispatch(&mut self, params: &Params, intermediates: &[u8], ignore: bool, c: u8) {
|
||
|
black_box((params, intermediates, ignore, c));
|
||
|
}
|
||
|
|
||
|
fn esc_dispatch(&mut self, intermediates: &[u8], ignore: bool, byte: u8) {
|
||
|
black_box((intermediates, ignore, byte));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#[derive(Default)]
|
||
|
struct Strip(String);
|
||
|
impl Strip {
|
||
|
fn with_capacity(capacity: usize) -> Self {
|
||
|
Self(String::with_capacity(capacity))
|
||
|
}
|
||
|
}
|
||
|
impl Perform for Strip {
|
||
|
fn print(&mut self, c: char) {
|
||
|
self.0.push(c);
|
||
|
}
|
||
|
|
||
|
fn execute(&mut self, byte: u8) {
|
||
|
if byte.is_ascii_whitespace() {
|
||
|
self.0.push(byte as char);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fn strip_str(content: &str) -> String {
|
||
|
use anstyle_parse::state::state_change;
|
||
|
use anstyle_parse::state::Action;
|
||
|
use anstyle_parse::state::State;
|
||
|
|
||
|
#[inline]
|
||
|
fn is_utf8_continuation(b: u8) -> bool {
|
||
|
matches!(b, 0x80..=0xbf)
|
||
|
}
|
||
|
|
||
|
#[inline]
|
||
|
fn is_printable(action: Action, byte: u8) -> bool {
|
||
|
action == Action::Print
|
||
|
|| action == Action::BeginUtf8
|
||
|
// since we know the input is valid UTF-8, the only thing we can do with
|
||
|
// continuations is to print them
|
||
|
|| is_utf8_continuation(byte)
|
||
|
|| (action == Action::Execute && byte.is_ascii_whitespace())
|
||
|
}
|
||
|
|
||
|
let mut stripped = Vec::with_capacity(content.len());
|
||
|
|
||
|
let mut bytes = content.as_bytes();
|
||
|
while !bytes.is_empty() {
|
||
|
let offset = bytes.iter().copied().position(|b| {
|
||
|
let (_next_state, action) = state_change(State::Ground, b);
|
||
|
!is_printable(action, b)
|
||
|
});
|
||
|
let (printable, next) = bytes.split_at(offset.unwrap_or(bytes.len()));
|
||
|
stripped.extend(printable);
|
||
|
bytes = next;
|
||
|
|
||
|
let mut state = State::Ground;
|
||
|
let offset = bytes.iter().copied().position(|b| {
|
||
|
let (next_state, action) = state_change(state, b);
|
||
|
if next_state != State::Anywhere {
|
||
|
state = next_state;
|
||
|
}
|
||
|
is_printable(action, b)
|
||
|
});
|
||
|
let (_, next) = bytes.split_at(offset.unwrap_or(bytes.len()));
|
||
|
bytes = next;
|
||
|
}
|
||
|
|
||
|
String::from_utf8(stripped).unwrap()
|
||
|
}
|
||
|
|
||
|
fn parse(c: &mut Criterion) {
|
||
|
for (name, content) in [
|
||
|
#[cfg(feature = "utf8")]
|
||
|
("demo.vte", &include_bytes!("../tests/demo.vte")[..]),
|
||
|
("rg_help.vte", &include_bytes!("../tests/rg_help.vte")[..]),
|
||
|
("rg_linus.vte", &include_bytes!("../tests/rg_linus.vte")[..]),
|
||
|
(
|
||
|
"state_changes",
|
||
|
&b"\x1b]2;X\x1b\\ \x1b[0m \x1bP0@\x1b\\"[..],
|
||
|
),
|
||
|
] {
|
||
|
// Make sure the comparison is fair
|
||
|
if let Ok(content) = std::str::from_utf8(content) {
|
||
|
let mut stripped = Strip::with_capacity(content.len());
|
||
|
let mut parser = Parser::<DefaultCharAccumulator>::new();
|
||
|
for byte in content.as_bytes() {
|
||
|
parser.advance(&mut stripped, *byte);
|
||
|
}
|
||
|
assert_eq!(stripped.0, strip_str(content));
|
||
|
}
|
||
|
|
||
|
let mut group = c.benchmark_group(name);
|
||
|
group.bench_function("advance", |b| {
|
||
|
b.iter(|| {
|
||
|
let mut dispatcher = BenchDispatcher;
|
||
|
let mut parser = Parser::<DefaultCharAccumulator>::new();
|
||
|
|
||
|
for byte in content {
|
||
|
parser.advance(&mut dispatcher, *byte);
|
||
|
}
|
||
|
})
|
||
|
});
|
||
|
group.bench_function("advance_strip", |b| {
|
||
|
b.iter(|| {
|
||
|
let mut stripped = Strip::with_capacity(content.len());
|
||
|
let mut parser = Parser::<DefaultCharAccumulator>::new();
|
||
|
|
||
|
for byte in content {
|
||
|
parser.advance(&mut stripped, *byte);
|
||
|
}
|
||
|
|
||
|
black_box(stripped.0)
|
||
|
})
|
||
|
});
|
||
|
group.bench_function("state_change", |b| {
|
||
|
b.iter(|| {
|
||
|
let mut state = anstyle_parse::state::State::default();
|
||
|
for byte in content {
|
||
|
let (next_state, action) = anstyle_parse::state::state_change(state, *byte);
|
||
|
state = next_state;
|
||
|
black_box(action);
|
||
|
}
|
||
|
})
|
||
|
});
|
||
|
if let Ok(content) = std::str::from_utf8(content) {
|
||
|
group.bench_function("state_change_strip_str", |b| {
|
||
|
b.iter(|| {
|
||
|
let stripped = strip_str(content);
|
||
|
|
||
|
black_box(stripped)
|
||
|
})
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
criterion::criterion_group!(benches, parse);
|
||
|
criterion::criterion_main!(benches);
|