use std::collections::HashMap;
use std::rc::Rc;
use std::sync::LazyLock;
use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main};
use std::hint::black_box;
use qualname::{NamespaceUri, NcName, QName};
use xrust::item::Node;
use xrust::parser::ParseError;
use xrust::parser::xml::parse;
use xrust::trees::smite::RNode;
use xrust::value::Value;
static NSURI: LazyLock<Option<NamespaceUri>> =
LazyLock::new(|| Some(NamespaceUri::try_from("http://www.example.com/namespace").unwrap()));
fn make_rnode(n: u64) -> RNode {
let mut a = RNode::new_document();
let mut b = a
.new_element(QName::from_local_name(NcName::try_from("Test").unwrap()))
.expect("unable to create element");
a.push(b.clone()).expect("unable to add node");
let l1name = QName::from_local_name(NcName::try_from("Level-1").unwrap());
let l2name = QName::from_local_name(NcName::try_from("Level-2").unwrap());
(1..n).for_each(|i| {
let mut l1 = a
.new_element(l1name.clone())
.expect("unable to create element");
b.push(l1.clone()).expect("unable to add node");
(1..n).for_each(|k| {
let mut l2 = a
.new_element(l2name.clone())
.expect("unable to create element");
l1.push(l2.clone()).expect("unable to add node");
l2.push(
a.new_text(Rc::new(Value::from(format!("node {}-{}", i, k))))
.expect("unable to create text node"),
)
.expect("unable to add node");
});
});
a
}
fn parse_doc(n: u64) -> RNode {
let mut a = String::from("<pre:top_level xmlns:pre='urn:benchmark.org'>\n");
(1..n).for_each(|i| {
a.push_str(" <pre:child>");
a.push_str(format!("{}", i).as_str());
a.push_str("</pre:child>\n");
});
a.push_str("</pre:top_level>\n");
let doc = RNode::new_document();
parse(
doc.clone(),
a.as_str(),
Some(|_: &_| Err(ParseError::MissingNameSpace)),
)
.expect("failed to parse XML");
doc
}
fn make_m_r_b(s: (usize, usize, &str)) -> RNode {
let (m, r, base) = s;
let mut names = HashMap::new();
let mut doc = RNode::new_document();
let mut top = doc
.new_element(QName::new_from_parts(
NcName::try_from("top").unwrap(),
NSURI.clone(),
))
.unwrap();
doc.push(top.clone()).unwrap();
for i in 0..m {
names.insert(
format!("{}{}", base, i),
QName::new_from_parts(
NcName::try_from(format!("{}{}", base, i).as_str()).unwrap(),
NSURI.clone(),
),
);
}
for _j in 0..r {
for i in 0..m {
let nd = doc
.new_element(names.get(&format!("{}{}", base, i)).unwrap().clone())
.unwrap();
top.push(nd).unwrap();
}
}
doc
}
fn search_nodes(s: (RNode, QName)) -> usize {
let (parent, name) = s;
parent
.child_iter()
.filter(|c| c.name().is_some_and(|nm| nm == name))
.collect::<Vec<RNode>>()
.len()
}
fn qname(c: &mut Criterion) {
let mut group = c.benchmark_group("smite");
group.bench_with_input(BenchmarkId::from_parameter("rnode1000"), &1000, |b, _| {
b.iter(|| make_rnode(black_box(1000)))
});
group.bench_with_input(BenchmarkId::from_parameter("parse100"), &100, |b, _| {
b.iter(|| parse_doc(black_box(100)))
});
group.bench_with_input(BenchmarkId::from_parameter("parse1000"), &1000, |b, _| {
b.iter(|| parse_doc(black_box(1000)))
});
group.bench_with_input(BenchmarkId::from_parameter("make100"), &100, |b, _| {
b.iter(|| make_m_r_b(black_box((100, 100, "basebasebasebase"))))
});
group.bench_with_input(BenchmarkId::from_parameter("make1000"), &1000, |b, _| {
b.iter(|| make_m_r_b(black_box((1000, 100, "basebasebasebase"))))
});
let doc100 = make_m_r_b((100, 100, "basebasebasebase"));
let doc1000 = make_m_r_b((1000, 100, "basebasebasebase"));
group.bench_with_input(BenchmarkId::from_parameter("search100"), &100, |b, _| {
b.iter(|| {
search_nodes(black_box((
doc100.clone(),
QName::new_from_parts(
NcName::try_from("basebasebasebase1").unwrap(),
NSURI.clone(),
),
)))
})
});
group.bench_with_input(BenchmarkId::from_parameter("search1000"), &1000, |b, _| {
b.iter(|| {
search_nodes(black_box((
doc1000.clone(),
QName::new_from_parts(
NcName::try_from("basebasebasebase1").unwrap(),
NSURI.clone(),
),
)))
})
});
}
criterion_group!(benches, qname);
criterion_main!(benches);