bonsai_bt/tracer.rs
1//! Always-compiled telemetry primitives. Lives outside `telemetry.rs` so
2//! `State::tick`'s signature carries the same `Tracer`/`NodeMeta` types whether
3//! or not the `visualize` feature is on.
4
5use crate::Status;
6
7/// Tick-time recording sink, monomorphized into `State::tick`.
8///
9/// `IS_RECORDING` is a const switch — when `false`, the optimizer constant-
10/// folds every `if T::IS_RECORDING { ... }` site to a no-op, allowing the
11/// child-id arithmetic and `metas` indexing to be dead-code-eliminated. Code
12/// reading `T::IS_RECORDING` MUST be inside an `if` so the compiler can elide
13/// the entire branch at monomorphization time.
14///
15/// Implementations should:
16/// - Set `IS_RECORDING = false` only for no-op tracers (use `#[inline(always)]`
17/// on `record` so the call disappears).
18/// - Set `IS_RECORDING = true` for any tracer that actually consumes the
19/// `(id, status)` pair.
20pub trait Tracer {
21 const IS_RECORDING: bool;
22 fn record(&mut self, id: usize, status: Status);
23}
24
25pub struct NoopTracer;
26impl Tracer for NoopTracer {
27 const IS_RECORDING: bool = false;
28 #[inline(always)]
29 fn record(&mut self, _id: usize, _status: Status) {}
30}
31
32/// Preorder metadata for one node — computed once at `BT::new`,
33/// tracers to cheaply advance the id counter past unvisited subtrees.
34#[derive(Clone, Debug)]
35pub struct NodeMeta {
36 /// Number of nodes in this subtree, including the root (self).
37 pub subtree_size: usize,
38}
39
40/// Compute the preorder id of the first child of `self_id`, or a sentinel
41/// when telemetry is off. Inlined; the const-fold of `T::IS_RECORDING` removes
42/// all arithmetic in the noop path.
43#[inline(always)]
44pub(crate) fn first_child_id<T: Tracer>(self_id: usize) -> usize {
45 if T::IS_RECORDING {
46 self_id + 1
47 } else {
48 usize::MAX
49 }
50}
51
52/// Compute the preorder id of `child_id`'s next sibling. `metas[child_id]` is
53/// only read when `T::IS_RECORDING`, so the noop path elides the index.
54#[inline(always)]
55pub(crate) fn next_sibling_id<T: Tracer>(metas: &[NodeMeta], child_id: usize) -> usize {
56 if T::IS_RECORDING {
57 child_id + metas[child_id].subtree_size
58 } else {
59 usize::MAX
60 }
61}