use std::sync::atomic::{AtomicU64, Ordering::Relaxed};
use std::sync::OnceLock;
use std::time::Instant;
#[derive(Clone, Copy)]
pub struct TraceCtx {
pub trace_id: [u8; 16],
pub span_id: [u8; 8],
}
static REQS: AtomicU64 = AtomicU64::new(0);
static INFLT: AtomicU64 = AtomicU64::new(0);
#[inline]
pub fn parse_traceparent(h: &[u8]) -> Option<TraceCtx> {
if h.len() < 55 || h[2] != b'-' || h[35] != b'-' || h[52] != b'-' {
return None;
}
let mut tid = [0u8; 16];
let mut sid = [0u8; 8];
hex_into(&h[3..35], &mut tid)?;
hex_into(&h[36..52], &mut sid)?;
Some(TraceCtx {
trace_id: tid,
span_id: sid,
})
}
#[inline]
fn hex_into(src: &[u8], dst: &mut [u8]) -> Option<()> {
for (i, byte) in dst.iter_mut().enumerate() {
*byte = (nyb(src[2 * i])? << 4) | nyb(src[2 * i + 1])?;
}
Some(())
}
#[inline]
fn nyb(c: u8) -> Option<u8> {
match c {
b'0'..=b'9' => Some(c - b'0'),
b'a'..=b'f' => Some(c - b'a' + 10),
b'A'..=b'F' => Some(c - b'A' + 10),
_ => None,
}
}
fn monotonic_nanos() -> u64 {
static EPOCH: OnceLock<Instant> = OnceLock::new();
EPOCH.get_or_init(Instant::now).elapsed().as_nanos() as u64
}
pub fn new_span_id() -> [u8; 8] {
static COUNTER: AtomicU64 = AtomicU64::new(1);
let count = COUNTER.fetch_add(1, Relaxed);
let mut v = monotonic_nanos() ^ count.wrapping_mul(0x9E3779B97F4A7C15);
v ^= v << 13;
v ^= v >> 7;
v ^= v << 17;
v.to_ne_bytes()
}
pub fn new_trace_id() -> [u8; 16] {
let lo = new_span_id();
let hi = new_span_id();
let mut out = [0u8; 16];
out[..8].copy_from_slice(&hi);
out[8..].copy_from_slice(&lo);
out
}
pub fn hex_encode(bytes: &[u8]) -> String {
bytes.iter().map(|b| format!("{b:02x}")).collect()
}
pub struct RequestGuard;
#[inline]
pub fn on_request_start() -> RequestGuard {
REQS.fetch_add(1, Relaxed);
INFLT.fetch_add(1, Relaxed);
RequestGuard
}
impl Drop for RequestGuard {
#[inline]
fn drop(&mut self) {
INFLT.fetch_sub(1, Relaxed);
}
}
#[inline]
pub fn snapshot() -> (u64, u64) {
(REQS.load(Relaxed), INFLT.load(Relaxed))
}