use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
use serde_json::json;
use rdap_core::validation::{validate_rdap_response, RdapValidationLimits};
fn minimal_domain_bytes() -> Vec<u8> {
json!({
"objectClassName": "domain",
"ldhName": "example.com",
"status": ["active"]
})
.to_string()
.into_bytes()
}
fn typical_domain_bytes() -> Vec<u8> {
json!({
"objectClassName": "domain",
"handle": "EXAMPLE-VERISIGN",
"ldhName": "example.com",
"status": ["client delete prohibited", "client transfer prohibited", "active"],
"nameservers": [
{ "objectClassName": "nameserver", "ldhName": "ns1.example.com" },
{ "objectClassName": "nameserver", "ldhName": "ns2.example.com" },
{ "objectClassName": "nameserver", "ldhName": "ns3.example.com" }
],
"entities": [
{
"objectClassName": "entity",
"handle": "R1-LROR",
"roles": ["registrar"],
"publicIds": [{ "type": "IANA Registrar ID", "identifier": "376" }],
"vcardArray": ["vcard", [
["version", {}, "text", "4.0"],
["fn", {}, "text", "ACME Registrar, Inc."],
["tel", { "type": ["voice"] }, "uri", "tel:+1.4805551212"]
]],
"entities": [{
"objectClassName": "entity",
"roles": ["abuse"],
"vcardArray": ["vcard", [
["version", {}, "text", "4.0"],
["fn", {}, "text", "Abuse Contact"]
]]
}]
},
{
"objectClassName": "entity",
"handle": "C100-LROR",
"roles": ["registrant", "technical"],
"vcardArray": ["vcard", [
["version", {}, "text", "4.0"],
["fn", {}, "text", "Example, Inc."],
["adr", {}, "text", ["", "", "1 Example St", "Anytown", "CA", "94025", "US"]]
]]
}
],
"events": [
{ "eventAction": "registration", "eventDate": "1995-08-14T04:00:00Z" },
{ "eventAction": "expiration", "eventDate": "2030-08-13T04:00:00Z" },
{ "eventAction": "last changed", "eventDate": "2024-01-15T12:00:00Z" },
{ "eventAction": "last update of RDAP database", "eventDate": "2024-03-20T00:00:00Z" }
],
"links": [
{
"value": "https://rdap.verisign.com/com/v1/domain/example.com",
"rel": "self",
"href": "https://rdap.verisign.com/com/v1/domain/example.com",
"type": "application/rdap+json"
}
],
"remarks": [{
"title": "Terms of Use",
"description": ["The data in this record is provided by Verisign."]
}]
})
.to_string()
.into_bytes()
}
fn large_domain_bytes(entity_count: usize) -> Vec<u8> {
let entities: Vec<serde_json::Value> = (0..entity_count)
.map(|i| {
json!({
"objectClassName": "entity",
"handle": format!("ENTITY-{i}"),
"roles": ["technical"],
"vcardArray": ["vcard", [
["version", {}, "text", "4.0"],
["fn", {}, "text", format!("Contact {i}")]
]],
"events": [{ "eventAction": "registration", "eventDate": "2020-01-01T00:00:00Z" }]
})
})
.collect();
json!({
"objectClassName": "domain",
"handle": "LARGE-DOMAIN",
"ldhName": "large.com",
"status": ["active"],
"nameservers": (0..8).map(|i| json!({
"objectClassName": "nameserver",
"ldhName": format!("ns{i}.large.com")
})).collect::<Vec<_>>(),
"entities": entities,
"events": (0..10).map(|i| json!({
"eventAction": format!("event-{i}"),
"eventDate": "2024-01-01T00:00:00Z"
})).collect::<Vec<_>>(),
"links": (0..5).map(|i| json!({
"value": format!("https://rdap.example.com/link/{i}"),
"rel": "related",
"href": format!("https://rdap.example.com/link/{i}"),
"type": "application/rdap+json"
})).collect::<Vec<_>>()
})
.to_string()
.into_bytes()
}
fn bench_validate_payload_sizes(c: &mut Criterion) {
let limits = RdapValidationLimits::default();
let minimal = minimal_domain_bytes();
let typical = typical_domain_bytes();
let large_20 = large_domain_bytes(20);
let large_50 = large_domain_bytes(50);
let mut group = c.benchmark_group("validation/full_pipeline");
group.bench_function("minimal_domain", |b| {
b.iter(|| {
criterion::black_box(
validate_rdap_response(&minimal, &limits).expect("validation failed"),
)
});
});
group.bench_function("typical_domain", |b| {
b.iter(|| {
criterion::black_box(
validate_rdap_response(&typical, &limits).expect("validation failed"),
)
});
});
group.bench_function("large_domain_20_entities", |b| {
b.iter(|| {
criterion::black_box(
validate_rdap_response(&large_20, &limits).expect("validation failed"),
)
});
});
group.bench_function("large_domain_50_entities", |b| {
b.iter(|| {
criterion::black_box(
validate_rdap_response(&large_50, &limits).expect("validation failed"),
)
});
});
group.finish();
}
fn bench_validate_limit_scenarios(c: &mut Criterion) {
let limits = RdapValidationLimits::default();
let payloads: Vec<(usize, Vec<u8>)> = [1, 10, 50, 100]
.iter()
.map(|&n| (n, large_domain_bytes(n)))
.collect();
let mut group = c.benchmark_group("validation/by_entity_count");
for (n, bytes) in &payloads {
group.bench_with_input(BenchmarkId::from_parameter(n), bytes, |b, payload| {
b.iter(|| {
criterion::black_box(
validate_rdap_response(payload, &limits).expect("validation failed"),
)
});
});
}
group.finish();
}
fn bench_validation_overhead_vs_raw_parse(c: &mut Criterion) {
let limits = RdapValidationLimits::default();
let bytes = typical_domain_bytes();
let mut group = c.benchmark_group("validation/overhead_comparison");
group.bench_function("raw_serde_parse", |b| {
b.iter(|| {
criterion::black_box(
serde_json::from_slice::<serde_json::Value>(&bytes).expect("parse failed"),
)
});
});
group.bench_function("validated_parse", |b| {
b.iter(|| {
criterion::black_box(
validate_rdap_response(&bytes, &limits).expect("validation failed"),
)
});
});
group.finish();
}
criterion_group!(
benches,
bench_validate_payload_sizes,
bench_validate_limit_scenarios,
bench_validation_overhead_vs_raw_parse,
);
criterion_main!(benches);