use criterion::{Criterion, black_box, criterion_group, criterion_main};
use rand::Rng;
use rand::rng;
use rumdl_lib::LintContext;
use rumdl_lib::MD053LinkImageReferenceDefinitions;
use rumdl_lib::rule::Rule;
fn create_test_content(size: usize, ratio: f64) -> String {
let mut content = String::with_capacity(size);
let mut rng = rng();
let line_length = 80;
let words_per_line = 10;
let word_length = line_length / words_per_line;
content.push_str("# Test Document with References\n\n");
for i in 0..100 {
if rng.random::<f64>() < 0.5 {
if rng.random::<f64>() < 0.7 {
content.push_str(&format!("[Link {i}][ref-{i}]\n"));
} else {
content.push_str(&format!("![Image {i}][ref-{i}]\n"));
}
}
}
content.push('\n');
let num_paragraphs = size / (line_length * 5);
for _ in 0..num_paragraphs {
let num_lines = rng.random_range(3..7);
for _ in 0..num_lines {
for _ in 0..words_per_line {
let word_size = rng.random_range(3..word_length);
for _ in 0..word_size {
let c = (b'a' + rng.random_range(0..26)) as char;
content.push(c);
}
content.push(' ');
}
content.push('\n');
}
content.push('\n');
}
for i in 0..100 {
if rng.random::<f64>() < ratio {
content.push_str(&format!("[ref-{i}]: https://example.com/ref-{i}\n"));
}
}
content
}
fn create_test_content_legacy() -> String {
let mut content = String::with_capacity(50_000);
content.push_str("## Reference Definitions\n\n");
for i in 0..200 {
if i < 100 {
content.push_str(&format!("[ref{i}]: https://example.com/ref{i}\n"));
} else {
content.push_str(&format!("[unused{i}]: https://example.com/unused{i}\n"));
}
}
content.push_str("\n## Content with References\n\n");
for i in 0..100 {
content.push_str(&format!("This is a paragraph with a [link][ref{i}] reference.\n"));
}
content
}
fn bench_md053_legacy(c: &mut Criterion) {
let content = create_test_content_legacy();
let rule = MD053LinkImageReferenceDefinitions::default();
c.bench_function("MD053 Legacy (line/col)", |b| {
b.iter(|| {
let ctx = LintContext::new(black_box(&content), rumdl_lib::config::MarkdownFlavor::Standard);
let warnings = rule.check(&ctx).unwrap();
warnings.iter().for_each(|w| {
if let Some(fix) = &w.fix {
let line = content[..fix.range.start].lines().count();
let col = fix.range.start - content[..fix.range.start].rfind('\n').unwrap_or(0);
let _ = (line, col); }
});
})
});
}
fn bench_md053_range_based(c: &mut Criterion) {
let content = create_test_content_legacy();
let rule = MD053LinkImageReferenceDefinitions::default();
c.bench_function("MD053 Byte Ranges", |b| {
b.iter(|| {
let ctx = LintContext::new(black_box(&content), rumdl_lib::config::MarkdownFlavor::Standard);
rule.check(&ctx).unwrap();
})
});
}
fn bench_md053_many_references(c: &mut Criterion) {
let content = create_test_content(50000, 0.8); let rule = MD053LinkImageReferenceDefinitions::default();
c.bench_function("MD053 Many References", |b| {
b.iter(|| {
let ctx = LintContext::new(&content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let _ = rule.check(&ctx);
});
});
}
fn bench_md053_fix_many_references(c: &mut Criterion) {
let content = create_test_content(50000, 0.4); let rule = MD053LinkImageReferenceDefinitions::default();
c.bench_function("MD053 Fix Many References", |b| {
b.iter(|| {
let ctx = LintContext::new(&content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let _ = rule.fix(&ctx);
});
});
}
criterion_group!(
benches,
bench_md053_legacy,
bench_md053_range_based,
bench_md053_many_references,
bench_md053_fix_many_references
);
criterion_main!(benches);