use std::net::SocketAddr;
use s0_const::{MAGIC_LEN, PROBE_ACK_MAGIC, PROBE_LEN, PROBE_MAGIC};
use s0_wire::{decode_socket_addrs, encode_socket_addrs};
pub const fn make_probe(ts_us: u64) -> [u8; PROBE_LEN] {
pack_probe(PROBE_MAGIC, ts_us)
}
fn handle_probe(pkt: &[u8]) -> Option<[u8; PROBE_LEN]> {
let (head, tail) = pkt.split_first_chunk::<MAGIC_LEN>()?;
if u32::from_le_bytes(*head) != PROBE_MAGIC {
return None;
}
let ts: &[u8; 8] = tail.try_into().ok()?;
Some(pack_probe(PROBE_ACK_MAGIC, u64::from_le_bytes(*ts)))
}
pub fn handle_probe_observed(pkt: &[u8], observed: SocketAddr) -> Option<Vec<u8>> {
let base = handle_probe(pkt)?;
let addr_bytes = encode_socket_addrs(&[observed]);
let mut out = Vec::with_capacity(PROBE_LEN + addr_bytes.len());
out.extend_from_slice(&base);
out.extend_from_slice(&addr_bytes);
Some(out)
}
pub fn parse_probe_ack_observed(pkt: &[u8], now_us: u64) -> Option<(u64, Option<SocketAddr>)> {
let (head, tail) = pkt.split_first_chunk::<MAGIC_LEN>()?;
if u32::from_le_bytes(*head) != PROBE_ACK_MAGIC {
return None;
}
let (ts, rest) = tail.split_first_chunk::<8>()?;
let rtt = now_us.saturating_sub(u64::from_le_bytes(*ts));
let observed = if rest.is_empty() {
None
} else {
decode_socket_addrs(rest)
.ok()
.and_then(|v| v.first().copied())
};
Some((rtt, observed))
}
const fn pack_probe(magic: u32, ts_us: u64) -> [u8; PROBE_LEN] {
let m = magic.to_le_bytes();
let t = ts_us.to_le_bytes();
[
m[0], m[1], m[2], m[3], t[0], t[1], t[2], t[3], t[4], t[5], t[6], t[7],
]
}