crates/nornir-docbook/examples/common/mod.rs
#![allow(dead_code)]
//! The corpus and the kernel probes both measurement examples run on.
//!
//! `bench.rs` asks what a render COSTS; `mem.rs` asks WHERE the memory goes.
//! They must ask it of the same documents or the two tables cannot be read
//! against each other, so the generator lives here and neither owns it.
use nornir_docbook::{ExportMeta, ImageSet};
use std::time::Duration;
/// A one-pixel PNG, so the corpus can carry real images rather than paths that
/// resolve to nothing.
pub const PNG_1PX: &[u8] = &[
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52,
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x06, 0x00, 0x00, 0x00, 0x1f, 0x15, 0xc4,
0x89, 0x00, 0x00, 0x00, 0x0a, 0x49, 0x44, 0x41, 0x54, 0x78, 0x9c, 0x63, 0x00, 0x01, 0x00, 0x00,
0x05, 0x00, 0x01, 0x0d, 0x0a, 0x2d, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae,
0x42, 0x60, 0x82,
];
/// One block of the corpus: everything a real document has, once.
///
/// `n` makes each block textually distinct, which matters more than it looks:
/// typst memoizes on content, so a corpus of a thousand identical paragraphs
/// would measure the memo rather than the layout.
pub fn block(n: usize) -> String {
format!(
"## Section {n}\n\n\
Prose with `snake_case_ident_{n}`, a [link](https://example.test/a_{n}), \
**strong**, *emphasis* and a MAX_BLOB_BYTES that must keep its underscores.\n\n\
### Detail {n}\n\n\
- first item {n}\n- second item with `code_{n}`\n- third\n\n\
1. step one\n2. step two\n\n\
| column | value | note |\n| --- | ---: | :-: |\n\
| alpha {n} | {n} | ok |\n| beta | {} | ok |\n\n\
```rust\nfn item_{n}() -> usize {{ let x = {n}; x * 2 }}\n```\n\n\
> A quoted line about {n}.\n\n",
n * 7
)
}
/// A document of about `target` bytes, with twenty images spread through it.
pub fn document(target: usize) -> String {
document_with(target, true)
}
/// The same document with no image references at all.
///
/// `mem.rs` needs it: the public [`md_to_typst`](nornir_docbook::export::md_to_typst)
/// door emits a root-anchored `#image("/…")` for a local path, which is the
/// right answer for a caller compiling against a root of its own and a
/// "file not found" for the rootless in-memory pipeline that example rebuilds.
/// A corpus with no pictures is the same document in every way the memory
/// question cares about — the twenty images are one pixel each — and it lets
/// the rebuild be compared to the shipped function byte for byte.
pub fn document_with(target: usize, with_images: bool) -> String {
let mut out = String::with_capacity(target + 4096);
out.push_str("# The Corpus\n\nA generated document.\n\n");
let mut n = 0;
while out.len() < target {
out.push_str(&block(n));
if with_images && n < 20 {
out.push_str(&format!("\n\n"));
}
n += 1;
}
out
}
pub fn images() -> ImageSet {
let mut set = ImageSet::new();
for i in 0..20 {
set.insert(&format!("docs/fig{i}.png"), PNG_1PX.to_vec());
}
set
}
pub fn meta() -> ExportMeta {
ExportMeta {
title: "corpus".into(),
version: String::new(),
generated: String::new(),
cover_image: String::new(),
}
}
/// p50 and p99 of a run of durations.
pub fn stats(mut v: Vec<Duration>) -> (Duration, Duration) {
v.sort();
let p = |q: f64| v[(((v.len() - 1) as f64) * q).round() as usize];
(p(0.50), p(0.99))
}
/// Peak resident set, in kB, from `/proc/self/status`.
///
/// `VmHWM` and not `VmRSS`: the question is what the process ever needed, and
/// the allocator hands pages back before this is read.
pub fn peak_rss_kb() -> u64 {
proc_status_kb("VmHWM:")
}
/// Resident set *right now*, in kB. The companion to [`peak_rss_kb`]: a stage
/// table needs the level as well as the high-water, because a stage that frees
/// what it took shows a flat peak and a falling level, and those are different
/// facts.
pub fn rss_kb() -> u64 {
proc_status_kb("VmRSS:")
}
fn proc_status_kb(field: &str) -> u64 {
std::fs::read_to_string("/proc/self/status")
.ok()
.and_then(|s| {
s.lines()
.find(|l| l.starts_with(field))?
.split_whitespace()
.nth(1)?
.parse()
.ok()
})
.unwrap_or(0)
}
pub fn ms(d: Duration) -> f64 {
d.as_secs_f64() * 1000.0
}
/// Bytes as MB, one decimal — the unit every memory row in these examples is
/// printed in.
pub fn mb(bytes: u64) -> String {
format!("{:.1}", bytes as f64 / 1_048_576.0)
}