use std::cell::Cell;
use std::thread::LocalKey;
use std::time::Duration;
use web_time::Instant;
#[cfg(not(target_arch = "wasm32"))]
pub(crate) fn profile_enabled() -> bool {
crate::debug_flag!("BREP_PROFILE")
}
#[cfg(target_arch = "wasm32")]
pub(crate) fn profile_enabled() -> bool {
false
}
thread_local! {
static PROFILE_SINK: std::cell::RefCell<Option<String>> = const { std::cell::RefCell::new(None) };
}
#[doc(hidden)]
pub(crate) fn profile_emit(args: std::fmt::Arguments<'_>) {
use std::fmt::Write;
PROFILE_SINK.with(|sink| {
let mut sink = sink.borrow_mut();
match sink.as_mut() {
Some(buffer) => {
let _ = buffer.write_fmt(args);
buffer.push('\n');
}
None => eprintln!("{args}"),
}
});
}
pub(crate) struct ProfileSink;
impl ProfileSink {
pub(crate) fn open() -> Option<Self> {
if !profile_enabled() {
return None;
}
PROFILE_SINK.with(|sink| {
let mut sink = sink.borrow_mut();
if sink.is_some() {
return None;
}
*sink = Some(String::with_capacity(16 * 1024));
Some(Self)
})
}
}
impl Drop for ProfileSink {
fn drop(&mut self) {
if let Some(buffer) = PROFILE_SINK.with(|sink| sink.borrow_mut().take()) {
if !buffer.is_empty() {
eprint!("{buffer}");
}
}
}
}
pub(crate) trait Counters: Copy + 'static {
fn armed(&self) -> bool;
}
#[inline]
pub(crate) fn update<T: Counters>(cell: &'static LocalKey<Cell<T>>, update: impl FnOnce(&mut T)) {
cell.with(|cell| {
let mut counters = cell.get();
if counters.armed() {
update(&mut counters);
cell.set(counters);
}
});
}
#[inline]
pub(crate) fn armed<T: Counters>(cell: &'static LocalKey<Cell<T>>) -> bool {
cell.with(|cell| cell.get().armed())
}
#[inline]
pub(crate) fn started<T: Counters>(cell: &'static LocalKey<Cell<T>>) -> Option<Instant> {
armed(cell).then(Instant::now)
}
#[inline]
pub(crate) fn elapsed_ms(started: Option<Instant>) -> f64 {
started.map_or(0.0, |s| s.elapsed().as_secs_f64() * 1_000.0)
}
#[inline]
pub(crate) fn lap(enabled: bool, started: &mut Option<Instant>) -> Duration {
if !enabled {
return Duration::ZERO;
}
let now = Instant::now();
let elapsed = started.map(|s| now - s).unwrap_or_default();
*started = Some(now);
elapsed
}