use std::fmt::Write as _;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::time::{Duration, Instant};
const BINARY: &str = env!("CARGO_BIN_EXE_ids-le");
static COUNTER: AtomicUsize = AtomicUsize::new(0);
const CEILING: Duration = Duration::from_millis(300);
const LINEARITY: f64 = 6.0;
const SEED: u64 = 20_260_812;
const FILES_PER_COPY: usize = 500;
fn enabled(name: &str) -> bool {
if std::env::var_os("IDS_LE_BUDGET").is_some() {
return true;
}
eprintln!("SKIPPED {name}: set IDS_LE_BUDGET to run it");
false
}
struct Seeded(u64);
impl Seeded {
fn next(&mut self) -> u64 {
self.0 ^= self.0 << 13;
self.0 ^= self.0 >> 7;
self.0 ^= self.0 << 17;
self.0
}
fn pick<'a, T>(&mut self, from: &'a [T]) -> &'a T {
let index = usize::try_from(self.next() % from.len() as u64).expect("an index");
&from[index]
}
}
struct Tree {
root: PathBuf,
}
impl Tree {
fn new(name: &str) -> Self {
let unique = COUNTER.fetch_add(1, Ordering::Relaxed);
let root = std::env::temp_dir().join(format!(
"ids-le-budget-{name}-{}-{unique}",
std::process::id()
));
let _ = std::fs::remove_dir_all(&root);
std::fs::create_dir_all(&root).expect("a temporary directory");
Self { root }
}
}
impl Drop for Tree {
fn drop(&mut self) {
let _ = std::fs::remove_dir_all(&self.root);
}
}
const SHAPES: [(&str, &str); 8] = [
(
"json",
"{{\"service\":{{\"id\":\"{U}\",\"digest\":\"{H}\"}},\"session\":{{\"ulid\":\"{L}\"}},\
\"documents\":[{{\"_id\":\"{O}\"}}],\"discord\":{{\"channel_id\":{S}}}}}\n",
),
(
"yaml",
"service:\n id: {U}\n digest: {H}\nsession:\n ulid: {L}\ndocuments:\n - _id: {O}\n\
discord:\n channel_id: {S}\n",
),
(
"toml",
"digest = \"{H}\"\n\n[service]\nid = \"{U}\"\n\n[session]\nulid = \"{L}\"\n\n\
[discord]\nchannel_id = {S}\n",
),
(
"ini",
"digest = {H}\n\n[service]\nid = {U}\n\n[session]\nulid = {L}\n\n[discord]\n\
channel_id = {S}\n",
),
(
"env",
"SERVICE_ID={U}\nSESSION_ULID=\"{L}\"\nDIGEST={H}\nexport DISCORD_CHANNEL_ID={S}\n",
),
(
"csv",
"name,service_id,channel_id\nalpha,{U},{S}\nbeta,{O},{S}\n",
),
(
"md",
"# Release\n\nThe service ran under {U} and the session was {L}. The document was\n\
{O}, and the digest {H}.\n",
),
(
"ts",
"export const SERVICE = \"{U}\";\nexport const SESSION = \"{L}\";\n\
// seen at {O}\nconst digest = \"{H}\";\n",
),
];
fn document(seeded: &mut Seeded, index: usize) -> (&'static str, String) {
let (extension, template) = *seeded.pick(&SHAPES);
let body = template
.replace("{U}", &format!("f47ac10b-58cc-4372-a567-{index:012x}"))
.replace("{L}", "01KZSM9K00ABCDEFGH12345678")
.replace("{O}", &format!("6a7bb780a1b2c3{index:010x}"))
.replace("{S}", "1536886938009600000")
.replace("{H}", &format!("5d41402abc4b2a76b9719{index:011x}"));
let mut content = String::with_capacity(body.len() * 6);
for _ in 0..6 {
content.push_str(&body);
}
(extension, content)
}
fn populate(root: &Path, prefix: &str, seeded: &mut Seeded) {
for index in 0..FILES_PER_COPY {
let (extension, content) = document(seeded, index);
let directory = root.join(format!("{prefix}/pkg{}", index % 25));
std::fs::create_dir_all(&directory).expect("a directory");
std::fs::write(directory.join(format!("file{index}.{extension}")), &content)
.expect("a file");
}
}
fn scan(root: &Path) -> Duration {
let started = Instant::now();
let output = Command::new(BINARY)
.arg(root)
.output()
.expect("the binary runs");
let elapsed = started.elapsed();
assert_eq!(
output.status.code(),
Some(0),
"the scan did not finish: {}",
String::from_utf8_lossy(&output.stderr)
);
assert!(
output.stdout.len() > 1000,
"the scan reported almost nothing, so it measured almost nothing"
);
elapsed
}
fn timed(root: &Path) -> Duration {
let _ = scan(root);
let first = scan(root);
let second = scan(root);
first.min(second)
}
fn crowded(count: usize, one_line: bool, unicode: bool) -> String {
let mut content = String::with_capacity(count * 50);
content.push('{');
if unicode {
content.push_str("\"note\":\"caf\u{e9} \u{1f3af}\",");
}
for index in 1..=count {
let _ = write!(
content,
"\"k{index}\":\"f47ac10b-58cc-4372-a567-{index:012x}\","
);
if !one_line {
content.push('\n');
}
}
content.push_str("\"end\":0}");
content
}
fn timed_document(name: &str, content: &str) -> Duration {
let tree = Tree::new(name);
let file = tree.root.join("crowded.json");
std::fs::write(&file, content).expect("a file");
let _ = scan(&file);
let first = scan(&file);
let second = scan(&file);
first.min(second)
}
fn ratio(one: Duration, four: Duration) -> f64 {
four.as_secs_f64() / one.as_secs_f64().max(0.000_001)
}
#[test]
fn a_five_hundred_file_corpus_scans_inside_its_budget() {
if !enabled("a_five_hundred_file_corpus_scans_inside_its_budget") {
return;
}
let tree = Tree::new("ceiling");
let mut seeded = Seeded(SEED);
populate(&tree.root, "one", &mut seeded);
let elapsed = timed(&tree.root);
println!("budget: {FILES_PER_COPY} files scanned in {elapsed:?} (seed {SEED})");
assert!(
elapsed <= CEILING,
"seed {SEED}: {FILES_PER_COPY} files took {elapsed:?}, over the {CEILING:?} ceiling — \
10x the recorded local measurement, so this is an order of magnitude, not variance"
);
}
#[test]
fn four_times_the_files_is_not_six_times_the_clock() {
if !enabled("four_times_the_files_is_not_six_times_the_clock") {
return;
}
let small = Tree::new("linear-one");
let large = Tree::new("linear-four");
let mut seeded = Seeded(SEED);
populate(&small.root, "one", &mut seeded);
for copy in 0..4 {
let mut same = Seeded(SEED);
populate(&large.root, &format!("copy{copy}"), &mut same);
}
let one = timed(&small.root);
let four = timed(&large.root);
let measured = ratio(one, four);
println!("budget: one tree {one:?}, four trees {four:?}, ratio {measured:.2}x (seed {SEED})");
assert!(
measured <= LINEARITY,
"seed {SEED}: four times the tree took {measured:.2}x as long ({one:?} then {four:?}), \
over the {LINEARITY}x allowance — that is the shape of something quadratic"
);
}
#[test]
fn four_times_the_identifiers_in_one_file_is_not_six_times_the_clock() {
if !enabled("four_times_the_identifiers_in_one_file_is_not_six_times_the_clock") {
return;
}
let one = timed_document("crowded-one", &crowded(20_000, false, false));
let four = timed_document("crowded-four", &crowded(80_000, false, false));
let measured = ratio(one, four);
println!("budget: 20,000 identifiers {one:?}, 80,000 {four:?}, ratio {measured:.2}x");
assert!(
measured <= LINEARITY,
"four times the identifiers in one file took {measured:.2}x as long \
({one:?} then {four:?}), over the {LINEARITY}x allowance"
);
}
#[test]
fn four_times_the_identifiers_on_one_unicode_line_is_not_six_times_the_clock() {
if !enabled("four_times_the_identifiers_on_one_unicode_line_is_not_six_times_the_clock") {
return;
}
let ascii_one = timed_document("line-ascii-one", &crowded(10_000, true, false));
let ascii_four = timed_document("line-ascii-four", &crowded(40_000, true, false));
let unicode_one = timed_document("line-unicode-one", &crowded(10_000, true, true));
let unicode_four = timed_document("line-unicode-four", &crowded(40_000, true, true));
let ascii = ratio(ascii_one, ascii_four);
let unicode = ratio(unicode_one, unicode_four);
println!(
"budget: one line, ascii {ascii_one:?} then {ascii_four:?} ({ascii:.2}x); \
unicode {unicode_one:?} then {unicode_four:?} ({unicode:.2}x)"
);
assert!(
unicode <= LINEARITY,
"four times the identifiers on one non-ASCII line took {unicode:.2}x as long \
({unicode_one:?} then {unicode_four:?}) — the column index is counting from the \
line start again"
);
assert!(
ascii <= LINEARITY,
"four times the identifiers on one ASCII line took {ascii:.2}x as long \
({ascii_one:?} then {ascii_four:?})"
);
let gap = ratio(ascii_four, unicode_four);
assert!(
gap <= LINEARITY,
"the same document with one accented character in it took {gap:.2}x as long \
({ascii_four:?} then {unicode_four:?})"
);
}