use criterion::{BatchSize, BenchmarkId, Criterion, criterion_group, criterion_main};
use hjkl_buffer::{Edit, MarkSnapshot, Position, UndoEntry, View};
use std::hint::black_box;
use std::time::{Duration, SystemTime};
const BASE_LINES: usize = 2_000;
const BASE_WIDTH: usize = 60;
const DEPTHS: [usize; 4] = [16, 64, 256, 1024];
const WALKBACK: usize = 20;
fn base_text() -> String {
let line: String = "the quick brown fox jumps over the lazy dog "
.chars()
.cycle()
.take(BASE_WIDTH)
.collect();
let mut text = String::with_capacity((BASE_WIDTH + 1) * BASE_LINES);
for i in 0..BASE_LINES {
text.push_str(&line);
if i + 1 < BASE_LINES {
text.push('\n');
}
}
text
}
fn build_history(base: &str, n: usize) -> View {
let mut view = View::from_str(base);
for i in 0..n {
let cursor = view.cursor();
view.push_undo_entry(UndoEntry {
rope: view.rope(),
cursor: (cursor.row, cursor.col),
timestamp: SystemTime::UNIX_EPOCH + Duration::from_secs(i as u64),
marks: MarkSnapshot::default(),
});
let row = i % BASE_LINES;
view.apply_edit(Edit::InsertStr {
at: Position::new(row, 0),
text: format!("e{i} "),
});
}
view
}
fn earlier(view: &View, live: ropey::Rope) -> Option<ropey::Rope> {
view.seq_earlier_step(live, (0, 0), MarkSnapshot::default())
.map(|e| e.rope)
}
fn bench_cold_jump_back(c: &mut Criterion) {
let text = base_text();
let mut group = c.benchmark_group("undo");
for n in DEPTHS {
group.bench_with_input(BenchmarkId::new("cold_jump_back", n), &n, |b, &n| {
b.iter_batched(
|| {
let view = build_history(&text, n);
let live = view.rope();
(view, live)
},
|(view, live)| {
let mut live = live;
let mut steps = 0usize;
while let Some(restored) = earlier(&view, live.clone()) {
live = restored;
steps += 1;
}
black_box(steps)
},
BatchSize::SmallInput,
)
});
}
group.finish();
}
fn bench_single_deep_jump(c: &mut Criterion) {
let text = base_text();
let mut group = c.benchmark_group("undo");
for n in DEPTHS.into_iter().filter(|&n| n > WALKBACK) {
group.bench_with_input(BenchmarkId::new("single_deep_jump", n), &n, |b, &n| {
b.iter_batched(
|| {
let view = build_history(&text, n);
let mut live = view.rope();
for _ in 0..WALKBACK {
live = earlier(&view, live).expect("history deeper than WALKBACK");
}
(view, live)
},
|(view, live)| black_box(earlier(&view, live).is_some()),
BatchSize::SmallInput,
)
});
}
group.finish();
}
criterion_group!(undo, bench_cold_jump_back, bench_single_deep_jump);
criterion_main!(undo);