use std::path::PathBuf;
use std::sync::atomic::{AtomicU64, Ordering};
const FILE_BUDGET_BYTES: u64 = 32 * 1024 * 1024;
static CREATED: AtomicU64 = AtomicU64::new(0);
static CREATE_MICROS: AtomicU64 = AtomicU64::new(0);
pub(crate) fn note_creation(micros: u64) {
CREATED.fetch_add(1, Ordering::Relaxed);
CREATE_MICROS.fetch_add(micros, Ordering::Relaxed);
}
pub(crate) fn report_init(disk: &str) {
let (created, micros) = (
CREATED.load(Ordering::Relaxed),
CREATE_MICROS.load(Ordering::Relaxed),
);
if created == 0 {
return;
}
tracing::info!(
"pipeline cache: {created} pipelines created ({:.0} ms) at renderer init, disk blob {disk}",
micros as f64 / 1000.0
);
}
fn enabled() -> bool {
!cfg!(test)
}
fn file_path(name: &str) -> Option<PathBuf> {
concinnity_host::store::paths::pipeline_cache_dir().map(|dir| dir.join(name))
}
pub(crate) fn load(name: &str) -> Option<Vec<u8>> {
if !enabled() {
return None;
}
load_in(&file_path(name)?)
}
fn load_in(path: &std::path::Path) -> Option<Vec<u8>> {
let bytes = std::fs::read(path).ok()?;
if bytes.is_empty() || bytes.len() as u64 > FILE_BUDGET_BYTES {
let _ = std::fs::remove_file(path);
return None;
}
Some(bytes)
}
pub(crate) fn store_if_grown(name: &str, bytes: &[u8], previous_len: usize) -> bool {
if !enabled() {
return false;
}
match file_path(name) {
Some(path) => store_in(&path, bytes, previous_len),
None => false,
}
}
fn store_in(path: &std::path::Path, bytes: &[u8], previous_len: usize) -> bool {
if bytes.is_empty() || bytes.len() <= previous_len {
return false;
}
let Some(dir) = path.parent() else {
return false;
};
if std::fs::create_dir_all(dir).is_err() {
return false;
}
let tmp = path.with_extension(format!("{}.tmp", std::process::id()));
if std::fs::write(&tmp, bytes).is_err() {
return false;
}
if std::fs::rename(&tmp, path).is_err() {
let _ = std::fs::remove_file(&tmp);
return false;
}
true
}
pub(crate) fn delete(name: &str) {
if let Some(path) = file_path(name) {
let _ = std::fs::remove_file(path);
}
}
#[cfg(test)]
mod tests {
use super::*;
fn temp_file(tag: &str) -> PathBuf {
std::env::temp_dir().join(format!("cn_pso_{tag}_{}", std::process::id()))
}
#[test]
fn a_blob_round_trips_through_a_file() {
let path = temp_file("roundtrip").join("adapter.bin");
let _ = std::fs::remove_dir_all(path.parent().unwrap());
assert!(store_in(&path, &[1, 2, 3], 0));
assert_eq!(load_in(&path), Some(vec![1, 2, 3]));
let leftovers = std::fs::read_dir(path.parent().unwrap())
.unwrap()
.flatten()
.filter(|e| e.path().extension().is_some_and(|x| x == "tmp"))
.count();
assert_eq!(leftovers, 0, "temp files must not survive a store");
std::fs::remove_dir_all(path.parent().unwrap()).unwrap();
}
#[test]
fn only_growth_rewrites_the_blob() {
let path = temp_file("growth").join("adapter.bin");
let _ = std::fs::remove_dir_all(path.parent().unwrap());
assert!(store_in(&path, &[5, 5], 0));
let first_write = std::fs::metadata(&path).unwrap().modified().unwrap();
assert!(!store_in(&path, &[5, 5], 2));
assert!(!store_in(&path, &[6, 5], 2), "reshuffled");
assert!(!store_in(&path, &[5], 2), "shrunk");
assert_eq!(
std::fs::metadata(&path).unwrap().modified().unwrap(),
first_write
);
assert!(store_in(&path, &[5, 5, 6], 2), "growth writes");
std::fs::remove_dir_all(path.parent().unwrap()).unwrap();
}
#[test]
fn an_empty_blob_is_neither_stored_nor_loaded() {
let path = temp_file("empty").join("adapter.bin");
let _ = std::fs::remove_dir_all(path.parent().unwrap());
assert!(!store_in(&path, &[], 0));
std::fs::create_dir_all(path.parent().unwrap()).unwrap();
std::fs::write(&path, []).unwrap();
assert_eq!(load_in(&path), None);
assert!(!path.exists(), "a truncated file is deleted on load");
std::fs::remove_dir_all(path.parent().unwrap()).unwrap();
}
#[test]
fn an_over_budget_blob_is_deleted_on_load() {
let path = temp_file("budget").join("adapter.bin");
let _ = std::fs::remove_dir_all(path.parent().unwrap());
std::fs::create_dir_all(path.parent().unwrap()).unwrap();
std::fs::write(&path, vec![0u8; (FILE_BUDGET_BYTES + 1) as usize]).unwrap();
assert_eq!(load_in(&path), None);
assert!(!path.exists());
std::fs::remove_dir_all(path.parent().unwrap()).unwrap();
}
}