use ada_url::{SchemeType, Url};
use criterion::{Criterion, Throughput, criterion_group, criterion_main};
use std::collections::{BTreeMap, HashMap};
use std::hint::black_box;
use std::num::Wrapping;
const SCHEME_OPTIONS: &[&str] = &["http", "https", "ftp", "ws", "wss", "file"];
const WEIGHTS_CUMULATIVE: &[u32] = &[20, 30, 40, 45, 50, 55];
const WEIGHT_TOTAL: u32 = 55;
struct Xorshift64 {
state: Wrapping<u64>,
}
impl Xorshift64 {
fn new(seed: u64) -> Self {
let mut s = Wrapping(seed);
s += Wrapping(0x9e3779b97f4a7c15u64);
s = (s ^ (s >> 30)) * Wrapping(0xbf58476d1ce4e5b9u64);
s = (s ^ (s >> 27)) * Wrapping(0x94d049bb133111ebu64);
s ^= s >> 31;
Self { state: s }
}
fn next_u64(&mut self) -> u64 {
let mut x = self.state;
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
self.state = x;
x.0
}
fn next_scheme(&mut self) -> &'static str {
let val = (self.next_u64() % WEIGHT_TOTAL as u64) as u32;
for (i, &cum) in WEIGHTS_CUMULATIVE.iter().enumerate() {
if val < cum {
return SCHEME_OPTIONS[i];
}
}
SCHEME_OPTIONS[0]
}
}
fn populate(count: usize) -> Vec<&'static str> {
let mut rng = Xorshift64::new(12345); (0..count).map(|_| rng.next_scheme()).collect()
}
#[inline]
fn get_scheme_type_naive(input: &str) -> Option<SchemeType> {
if input == "http" {
Some(SchemeType::Http)
} else if input == "https" {
Some(SchemeType::Https)
} else if input == "ftp" {
Some(SchemeType::Ftp)
} else if input == "ws" {
Some(SchemeType::Ws)
} else if input == "wss" {
Some(SchemeType::Wss)
} else if input == "file" {
Some(SchemeType::File)
} else {
None
}
}
#[inline]
fn get_scheme_type_ada(scheme: &str) -> Option<SchemeType> {
let url_str = format!("{}://example.com/", scheme);
Url::parse(&url_str, None).ok().map(|u| u.scheme_type())
}
const NUM_STRINGS: usize = 200_000;
pub fn bench_scheme_naive(c: &mut Criterion) {
let strings = populate(NUM_STRINGS);
let bytes: u64 = strings.iter().map(|s| s.len() as u64).sum();
let mut group = c.benchmark_group("scheme_detection");
group.throughput(Throughput::Bytes(bytes));
group.bench_function("naive", |b| {
b.iter(|| {
let mut count = 0usize;
for &s in &strings {
if let Some(_t) = get_scheme_type_naive(black_box(s)) {
count += 1;
}
}
black_box(count)
})
});
let hash_map: HashMap<&str, SchemeType> = [
("http", SchemeType::Http),
("https", SchemeType::Https),
("ftp", SchemeType::Ftp),
("ws", SchemeType::Ws),
("wss", SchemeType::Wss),
("file", SchemeType::File),
]
.into_iter()
.collect();
group.bench_function("hash_map", |b| {
b.iter(|| {
let mut count = 0usize;
for &s in &strings {
if hash_map.get(black_box(s)).is_some() {
count += 1;
}
}
black_box(count)
})
});
let btree_map: BTreeMap<&str, SchemeType> = [
("http", SchemeType::Http),
("https", SchemeType::Https),
("ftp", SchemeType::Ftp),
("ws", SchemeType::Ws),
("wss", SchemeType::Wss),
("file", SchemeType::File),
]
.into_iter()
.collect();
group.bench_function("btree_map", |b| {
b.iter(|| {
let mut count = 0usize;
for &s in &strings {
if btree_map.get(black_box(s)).is_some() {
count += 1;
}
}
black_box(count)
})
});
group.bench_function("ada_url_parse", |b| {
b.iter(|| {
let mut count = 0usize;
for &s in &strings {
if get_scheme_type_ada(black_box(s)).is_some() {
count += 1;
}
}
black_box(count)
})
});
group.finish();
}
criterion_group!(benches, bench_scheme_naive);
criterion_main!(benches);