use crate::mass_properties::TwoLane;
use rustc_hash::FxHashSet;
use std::cell::{Cell, RefCell};
const CAPACITY: usize = 1 << 18;
thread_local! {
static MEMO: RefCell<FxHashSet<[u64; 2]>> = RefCell::new(FxHashSet::default());
static ENABLED: Cell<Option<bool>> = const { Cell::new(None) };
static HITS: Cell<u64> = const { Cell::new(0) };
static STORED: Cell<u64> = const { Cell::new(0) };
}
#[cfg(not(target_arch = "wasm32"))]
fn enabled_from_env() -> bool {
!matches!(std::env::var("BREP_SOUNDNESS_PAIR_MEMO").as_deref(), Ok("0") | Ok("off"))
}
#[cfg(target_arch = "wasm32")]
fn enabled_from_env() -> bool {
true
}
pub(crate) fn enabled() -> bool {
ENABLED.with(|cell| match cell.get() {
Some(enabled) => enabled,
None => {
let enabled = enabled_from_env();
cell.set(Some(enabled));
enabled
}
})
}
pub(crate) fn options_key(chord_tolerance: f64, weld: f64, min_crossing_length: f64) -> [u64; 2] {
let mut lane = TwoLane::new();
lane.f64(chord_tolerance);
lane.f64(weld);
lane.f64(min_crossing_length);
lane.finish()
}
pub(crate) fn pair_key(options: [u64; 2], a: [u64; 2], b: [u64; 2]) -> [u64; 2] {
let (low, high) = if a <= b { (a, b) } else { (b, a) };
let mut lane = TwoLane::new();
lane.u64(options[0]);
lane.u64(options[1]);
lane.u64(low[0]);
lane.u64(low[1]);
lane.u64(high[0]);
lane.u64(high[1]);
lane.finish()
}
pub(crate) fn contains(key: [u64; 2]) -> bool {
let hit = MEMO.with(|memo| memo.borrow().contains(&key));
if hit {
HITS.with(|cell| cell.set(cell.get() + 1));
}
hit
}
pub(crate) fn store(keys: impl IntoIterator<Item = [u64; 2]>) {
MEMO.with(|memo| {
let mut memo = memo.borrow_mut();
for key in keys {
if memo.len() >= CAPACITY {
memo.clear();
}
if memo.insert(key) {
STORED.with(|cell| cell.set(cell.get() + 1));
}
}
});
}
pub fn clear() {
MEMO.with(|memo| memo.borrow_mut().clear());
}
pub(crate) fn take_counts() -> (u64, u64, usize) {
let hits = HITS.with(|cell| cell.replace(0));
let stored = STORED.with(|cell| cell.replace(0));
(hits, stored, MEMO.with(|memo| memo.borrow().len()))
}