Skip to main content

sedsnet/
wire_format.rs

1//! Wire-format packing and unpacking of telemetry packets.
2//!
3//! This module defines the compact v2 wire format used to send and receive
4//! [`Packet`]s, along with:
5//! - [`pack_packet`] / [`unpack_packet`] for full packets.
6//! - [`peek_envelope`] for header-only inspection without touching the payload.
7//! - Size helpers like [`header_size_bytes`] and [`packet_wire_size`].
8//!
9//! The core public type here is [`TelemetryEnvelope`], a lightweight view of
10//! the header fields used by `peek_envelope`.
11
12use crate::MessageElement;
13use crate::{
14    DataEndpoint, TelemetryError, TelemetryResult, config::DataType, is_reliable_type,
15    message_meta, packet::Packet,
16};
17
18use crate::config::StandardSmallPayload;
19use crate::packet::{hash_bytes_u64, sender_address_u32};
20#[cfg(feature = "std")]
21use alloc::collections::BTreeMap;
22#[cfg(feature = "cryptography")]
23use alloc::vec;
24use alloc::{sync::Arc, vec::Vec};
25use crc32fast::Hasher as Crc32Hasher;
26#[cfg(feature = "std")]
27use std::sync::{Mutex, OnceLock};
28
29/// Lightweight header-only view of a packed [`Packet`].
30///
31/// Produced by [`peek_envelope`] without allocating or copying the payload.
32#[derive(Clone, Debug, PartialEq, Eq)]
33pub struct TelemetryEnvelope {
34    /// Telemetry [`DataType`] discriminant.
35    pub ty: DataType,
36    /// All endpoints this packet is destined for (set bits in the bitmap).
37    pub endpoints: Arc<[DataEndpoint]>,
38    /// Sender identity resolved from discovery/config address state.
39    pub sender: Arc<str>,
40    /// Compact source address carried by the packet header.
41    pub source_address: u32,
42    /// Timestamp in milliseconds (as stored on the wire).
43    pub timestamp_ms: u64,
44    /// Inline wire-format payload shape, if present.
45    pub wire_shape: Option<MessageElement>,
46    /// Frozen destination sender hashes, if present.
47    pub target_senders: Arc<[u64]>,
48}
49
50/// Reliable header included for data types marked `reliable` in the schema.
51#[derive(Clone, Copy, Debug, PartialEq, Eq)]
52pub struct ReliableHeader {
53    pub flags: u8,
54    pub seq: u32,
55    pub ack: u32,
56}
57
58/// Reliable header flag: frame is ACK-only control (no payload).
59pub const RELIABLE_FLAG_ACK_ONLY: u8 = 0x01;
60
61/// Reliable header flag: frame is reliable but unordered (ACK/retransmit without ordering).
62pub const RELIABLE_FLAG_UNORDERED: u8 = 0x02;
63
64/// Reliable header flag: frame is unsequenced best-effort (no ordering/ack).
65pub const RELIABLE_FLAG_UNSEQUENCED: u8 = 0x80;
66
67/// Fixed size of the reliable header on the wire.
68pub const RELIABLE_HEADER_BYTES: usize = 1 + 4 + 4;
69/// Fixed size of the CRC32 trailer on the wire.
70pub const CRC32_BYTES: usize = 4;
71
72// packet Layout:
73//
74//   [FLAGS: u8]
75//       Bit 0: payload compressed flag (1 = compressed)
76//       Bit 1: reliable header present
77//       Bit 7: explicit endpoint ID list present (mutually exclusive with bit5)
78//       Bit 5: endpoint bitmap present (0 = derive endpoints from data type metadata)
79//       Bit 6: compact reliable header present (if a reliable header exists)
80//
81//   [NEP: u8]
82//       Number of selected endpoints.
83//
84//   VARINT(ty: u32 as u64)           -- ULEB128
85//   VARINT(data_size: u64)           -- ULEB128   (LOGICAL payload size, uncompressed)
86//   VARINT(timestamp: u64)           -- ULEB128
87//   [VARINT(nonce: u16 as u64)]      -- ULEB128   (ONLY if bit3 set)
88//   VARINT(src_addr: u32 as u64)     -- ULEB128
89//
90//   [ENDPOINTS_BITMAP]               -- ONLY if bit5 set; 1 bit per possible endpoint
91//   [ENDPOINT_ID_LIST]               -- NEP sorted unique ULEB128 IDs if bit7 set
92//   [RELIABLE HEADER]                -- declared by bit1 or the wire contract
93//       [REL_FLAGS: u8]
94//       [SEQ: u32 LE]
95//       [ACK: u32 LE]
96//   PAYLOAD BYTES                    -- raw or compressed payload bytes
97//   [CRC32: u32 LE]                  -- checksum of all prior bytes
98
99// ===========================================================================
100// ULEB128 (varint) encoding helpers
101// ===========================================================================
102const FLAG_COMPRESSED_PAYLOAD: u8 = 0x01;
103const FLAG_RELIABLE_HEADER_PRESENT: u8 = 0x02;
104const FLAG_WIRE_CONTRACT: u8 = 0x04;
105const FLAG_PACKET_NONCE: u8 = 0x08;
106#[cfg(feature = "cryptography")]
107const FLAG_E2E_ENCRYPTED_PAYLOAD: u8 = 0x10;
108const FLAG_ENDPOINT_BITMAP_PRESENT: u8 = 0x20;
109const FLAG_ENDPOINT_LIST_PRESENT: u8 = 0x80;
110const FLAG_COMPACT_RELIABLE_HEADER: u8 = 0x40;
111const CONTRACT_FLAG_TARGETS: u8 = 0x01;
112const CONTRACT_FLAG_SHAPE: u8 = 0x02;
113const CONTRACT_FLAG_RELIABLE_HEADER: u8 = 0x04;
114const RELIABLE_WIRE_FLAG_SEQ_PRESENT: u8 = 0x04;
115const RELIABLE_WIRE_FLAG_ACK_PRESENT: u8 = 0x08;
116const RELIABLE_PUBLIC_FLAGS_MASK: u8 =
117    RELIABLE_FLAG_ACK_ONLY | RELIABLE_FLAG_UNORDERED | RELIABLE_FLAG_UNSEQUENCED;
118#[cfg(feature = "cryptography")]
119const E2E_NONCE_LEN: usize = 12;
120#[cfg(feature = "cryptography")]
121const E2E_TAG_CAP: usize = 32;
122
123#[derive(Clone, Debug, PartialEq, Eq)]
124struct WireContract {
125    shape: Option<MessageElement>,
126    target_senders: Option<Arc<[u64]>>,
127    has_reliable_header: bool,
128}
129
130/// Encode a `u64` as ULEB128 and append it to `out`.
131#[inline]
132fn write_uleb128<T>(mut v: u64, out: &mut Vec<T>)
133where
134    T: From<u8>,
135{
136    loop {
137        let mut byte = (v & 0x7F) as u8;
138        v >>= 7;
139        if v != 0 {
140            byte |= 0x80;
141        }
142        out.push(T::from(byte));
143        if v == 0 {
144            break;
145        }
146    }
147}
148
149/// Decode a ULEB128-encoded `u64` from the given reader.
150#[inline]
151fn read_uleb128(r: &mut ByteReader) -> Result<u64, TelemetryError> {
152    crate::varint::read_u64(r.buf, &mut r.off)
153}
154
155/// Compute the encoded length (in bytes) of a ULEB128-encoded `u64`.
156#[inline]
157fn uleb128_size(mut v: u64) -> usize {
158    let mut n = 1;
159    while v >= 0x80 {
160        v >>= 7;
161        n += 1;
162    }
163    n
164}
165
166/// Count the total number of bits set across all bytes of the bitmap.
167#[inline]
168fn bitmap_popcount(bm: &[u8]) -> usize {
169    bm.iter().map(|b| b.count_ones() as usize).sum()
170}
171
172// ===========================================================================
173// ByteReader: tiny cursor over a byte slice
174// ===========================================================================
175
176#[derive(Clone, Copy)]
177struct ByteReader<'a> {
178    buf: &'a [u8],
179    off: usize,
180}
181
182impl<'a> ByteReader<'a> {
183    /// Create a new reader over the given buffer starting at offset 0.
184    fn new(buf: &'a [u8]) -> Self {
185        Self { buf, off: 0 }
186    }
187
188    /// Remaining bytes that can still be read.
189    fn remaining(&self) -> usize {
190        self.buf.len().saturating_sub(self.off)
191    }
192
193    /// Read exactly `n` bytes, advancing the internal offset.
194    #[inline(always)]
195    fn read_bytes(&mut self, n: usize) -> Result<&'a [u8], TelemetryError> {
196        if self.remaining() < n {
197            return Err(TelemetryError::Unpack("short read"));
198        }
199        let s = &self.buf[self.off..self.off + n];
200        self.off += n;
201        Ok(s)
202    }
203}
204
205#[inline]
206fn write_u32_le(v: u32, out: &mut Vec<u8>) {
207    out.extend_from_slice(&v.to_le_bytes());
208}
209
210#[inline]
211fn read_u32_le(r: &mut ByteReader) -> Result<u32, TelemetryError> {
212    let b = r.read_bytes(4)?;
213    Ok(u32::from_le_bytes([b[0], b[1], b[2], b[3]]))
214}
215
216#[inline]
217fn write_wire_shape(shape: MessageElement, out: &mut Vec<u8>) -> Result<(), TelemetryError> {
218    let dt = crate::config::message_data_type_code(shape.data_type());
219    let class = crate::config::message_class_code(shape.message_type());
220    let mut packed = dt | (class << 4);
221    if matches!(shape, MessageElement::Static(_, _, _)) {
222        packed |= 1 << 6;
223    }
224    out.push(packed);
225    if let MessageElement::Static(count, _, _) = shape {
226        let count = u64::try_from(count).map_err(|_| TelemetryError::Pack("wire shape count"))?;
227        write_uleb128(count, out);
228    }
229    Ok(())
230}
231
232#[inline]
233fn decode_wire_shape(r: &mut ByteReader) -> Result<MessageElement, TelemetryError> {
234    let packed = r.read_bytes(1)?[0];
235    let dt = crate::config::message_data_type_from_code(packed & 0x0F)
236        .ok_or(TelemetryError::Unpack("wire shape type"))?;
237    let class = crate::config::message_class_from_code((packed >> 4) & 0x03)
238        .ok_or(TelemetryError::Unpack("wire shape class"))?;
239    if (packed & (1 << 6)) != 0 {
240        let count = usize::try_from(read_uleb128(r)?)
241            .map_err(|_| TelemetryError::Unpack("wire shape count"))?;
242        Ok(MessageElement::Static(count, dt, class))
243    } else {
244        Ok(MessageElement::Dynamic(dt, class))
245    }
246}
247
248#[inline]
249fn write_wire_contract(
250    shape: Option<MessageElement>,
251    target_senders: &[u64],
252    has_reliable_header: bool,
253    out: &mut Vec<u8>,
254) -> Result<(), TelemetryError> {
255    // Keep the contract self-contained and compact. It exists only to preserve
256    // delivery/decode intent for packets that are already in flight while
257    // schema or topology updates are still converging.
258    let mut flags = 0u8;
259    if !target_senders.is_empty() {
260        flags |= CONTRACT_FLAG_TARGETS;
261    }
262    if shape.is_some() {
263        flags |= CONTRACT_FLAG_SHAPE;
264    }
265    if has_reliable_header {
266        flags |= CONTRACT_FLAG_RELIABLE_HEADER;
267    }
268    out.push(flags);
269    if let Some(shape) = shape {
270        write_wire_shape(shape, out)?;
271    }
272    if !target_senders.is_empty() {
273        write_uleb128(target_senders.len() as u64, out);
274        for hash in target_senders {
275            out.extend_from_slice(&hash.to_le_bytes());
276        }
277    }
278    Ok(())
279}
280
281/// Size the frozen contract without constructing temporary shape/target buffers.
282fn wire_contract_size(shape: Option<MessageElement>, target_senders: &[u64]) -> usize {
283    1 + shape.map_or(0, |shape| match shape {
284        MessageElement::Static(count, _, _) => 1 + uleb128_size(count as u64),
285        _ => 1,
286    }) + if target_senders.is_empty() {
287        0
288    } else {
289        uleb128_size(target_senders.len() as u64) + target_senders.len() * 8
290    }
291}
292
293#[inline]
294fn decode_wire_contract(
295    r: &mut ByteReader,
296    has_contract: bool,
297) -> Result<WireContract, TelemetryError> {
298    if !has_contract {
299        return Ok(WireContract {
300            shape: None,
301            target_senders: None,
302            has_reliable_header: false,
303        });
304    }
305    let contract_len = usize::try_from(read_uleb128(r)?)
306        .map_err(|_| TelemetryError::Unpack("wire contract length"))?;
307    let contract_bytes = r.read_bytes(contract_len)?;
308    let mut cr = ByteReader::new(contract_bytes);
309    // Parsing through a bounded sub-reader lets us reject trailing garbage in
310    // the contract cleanly instead of accidentally treating it as payload or
311    // reliable-header bytes.
312    let flags = cr.read_bytes(1)?[0];
313    let shape = if (flags & CONTRACT_FLAG_SHAPE) != 0 {
314        Some(decode_wire_shape(&mut cr)?)
315    } else {
316        None
317    };
318    let target_senders: Option<Arc<[u64]>> = if (flags & CONTRACT_FLAG_TARGETS) != 0 {
319        let count = usize::try_from(read_uleb128(&mut cr)?)
320            .map_err(|_| TelemetryError::Unpack("wire contract target count"))?;
321        if count > cr.remaining() / 8 {
322            return Err(TelemetryError::Unpack("wire contract target count"));
323        }
324        if count == 0 {
325            None
326        } else {
327            let mut targets = Arc::<[u64]>::new_uninit_slice(count);
328            for slot in Arc::get_mut(&mut targets).expect("new target storage is unique") {
329                let bytes = cr.read_bytes(8)?;
330                slot.write(u64::from_le_bytes([
331                    bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
332                ]));
333            }
334            // The bounded target count guarantees every slot was initialized.
335            Some(unsafe { targets.assume_init() })
336        }
337    } else {
338        None
339    };
340    if cr.remaining() != 0 {
341        return Err(TelemetryError::Unpack("wire contract trailing bytes"));
342    }
343    Ok(WireContract {
344        shape,
345        target_senders,
346        has_reliable_header: (flags & CONTRACT_FLAG_RELIABLE_HEADER) != 0,
347    })
348}
349
350#[inline]
351fn crc32_bytes(data: &[u8]) -> u32 {
352    let mut hasher = Crc32Hasher::new();
353    hasher.update(data);
354    hasher.finalize()
355}
356
357#[inline]
358fn append_crc32(out: &mut Vec<u8>) {
359    let crc = crc32_bytes(out);
360    out.extend_from_slice(&crc.to_le_bytes());
361}
362
363// Initialize every byte before publishing the shared frame. Keeping the
364// payload separate avoids a second full-size allocation during Arc creation.
365fn join_frame_with_crc(header: &[u8], payload: &[u8]) -> Arc<[u8]> {
366    let mut hasher = Crc32Hasher::new();
367    hasher.update(header);
368    hasher.update(payload);
369    let crc = hasher.finalize().to_le_bytes();
370    let mut frame = Arc::<[u8]>::new_uninit_slice(header.len() + payload.len() + crc.len());
371    let bytes = Arc::get_mut(&mut frame).unwrap();
372    for (slot, value) in bytes
373        .iter_mut()
374        .zip(header.iter().chain(payload).chain(&crc))
375    {
376        slot.write(*value);
377    }
378    // SAFETY: the iterator above writes exactly header + payload + CRC bytes,
379    // which is the full allocation length. No uninitialized byte escapes.
380    unsafe { frame.assume_init() }
381}
382
383#[inline]
384fn split_crc32(buf: &[u8]) -> Result<(&[u8], u32), TelemetryError> {
385    if buf.len() < CRC32_BYTES {
386        return Err(TelemetryError::Unpack("short buffer"));
387    }
388    let data_len = buf.len() - CRC32_BYTES;
389    let crc = u32::from_le_bytes([
390        buf[data_len],
391        buf[data_len + 1],
392        buf[data_len + 2],
393        buf[data_len + 3],
394    ]);
395    Ok((&buf[..data_len], crc))
396}
397
398#[inline]
399fn verify_crc32(buf: &[u8]) -> Result<&[u8], TelemetryError> {
400    let (data, expected) = split_crc32(buf)?;
401    let actual = crc32_bytes(data);
402    if actual != expected {
403        return Err(TelemetryError::Unpack("crc32 mismatch"));
404    }
405    Ok(data)
406}
407
408#[cfg(feature = "cryptography")]
409#[inline]
410fn e2e_nonce_for_packet(pkt: &Packet) -> [u8; E2E_NONCE_LEN] {
411    let mut nonce = [0u8; E2E_NONCE_LEN];
412    nonce[..4].copy_from_slice(&pkt.data_type().as_u32().to_le_bytes());
413    nonce[4..10].copy_from_slice(&(pkt.timestamp() & 0x0000_FFFF_FFFF_FFFF).to_le_bytes()[..6]);
414    nonce[10..].copy_from_slice(&pkt.nonce().to_le_bytes());
415    nonce
416}
417
418#[cfg(feature = "cryptography")]
419fn write_encrypted_payload(
420    pkt: &Packet,
421    key_id: u32,
422    plaintext_wire_payload: &[u8],
423    out: &mut Vec<u8>,
424) -> TelemetryResult<()> {
425    let aad_end = out.len();
426    let nonce = e2e_nonce_for_packet(pkt);
427    let mut ciphertext = vec![0u8; plaintext_wire_payload.len()];
428    let mut tag = [0u8; E2E_TAG_CAP];
429    let (ciphertext_len, tag_len) = crate::crypto::seal_with_registered_crypto(
430        key_id,
431        &nonce,
432        &out[..aad_end],
433        plaintext_wire_payload,
434        &mut ciphertext,
435        &mut tag,
436    )?;
437    if ciphertext_len > ciphertext.len() || tag_len > tag.len() {
438        return Err(TelemetryError::SizeMismatchError);
439    }
440    write_uleb128(u64::from(key_id), out);
441    write_uleb128(plaintext_wire_payload.len() as u64, out);
442    write_uleb128(nonce.len() as u64, out);
443    out.extend_from_slice(&nonce);
444    write_uleb128(tag_len as u64, out);
445    out.extend_from_slice(&tag[..tag_len]);
446    out.extend_from_slice(&ciphertext[..ciphertext_len]);
447    Ok(())
448}
449
450#[cfg(feature = "cryptography")]
451fn read_encrypted_payload(
452    r: &mut ByteReader,
453    aad: &[u8],
454    plaintext_len: usize,
455) -> TelemetryResult<Vec<u8>> {
456    let key_id = u32::try_from(read_uleb128(r)?)
457        .map_err(|_| TelemetryError::Unpack("e2e key id too large"))?;
458    let wire_payload_len = usize::try_from(read_uleb128(r)?)
459        .map_err(|_| TelemetryError::Unpack("e2e payload length"))?;
460    if wire_payload_len > plaintext_len {
461        return Err(TelemetryError::Unpack("bad e2e payload length"));
462    }
463    let nonce_len = usize::try_from(read_uleb128(r)?)
464        .map_err(|_| TelemetryError::Unpack("e2e nonce length"))?;
465    if nonce_len == 0 || nonce_len > 64 {
466        return Err(TelemetryError::Unpack("bad e2e nonce length"));
467    }
468    let nonce = r.read_bytes(nonce_len)?;
469    let tag_len =
470        usize::try_from(read_uleb128(r)?).map_err(|_| TelemetryError::Unpack("e2e tag length"))?;
471    if tag_len == 0 || tag_len > E2E_TAG_CAP {
472        return Err(TelemetryError::Unpack("bad e2e tag length"));
473    }
474    let tag = r.read_bytes(tag_len)?;
475    let ciphertext_len = r.remaining();
476    let ciphertext = r.read_bytes(ciphertext_len)?;
477    let mut plaintext = vec![0u8; wire_payload_len];
478    let opened_len = crate::crypto::open_with_registered_crypto(
479        key_id,
480        nonce,
481        aad,
482        ciphertext,
483        tag,
484        &mut plaintext,
485    )?;
486    if opened_len != wire_payload_len {
487        return Err(TelemetryError::SizeMismatchError);
488    }
489    Ok(plaintext)
490}
491
492#[inline]
493fn write_reliable_header(h: ReliableHeader, out: &mut Vec<u8>) {
494    out.push(h.flags);
495    write_u32_le(h.seq, out);
496    write_u32_le(h.ack, out);
497}
498
499#[inline]
500fn reliable_compact_size(h: ReliableHeader) -> usize {
501    let seq_present = (h.flags & RELIABLE_FLAG_ACK_ONLY) == 0 || h.seq != 0;
502    let ack_present = h.ack != 0 || (h.flags & RELIABLE_FLAG_ACK_ONLY) != 0;
503    1 + if seq_present {
504        uleb128_size(h.seq as u64)
505    } else {
506        0
507    } + if ack_present {
508        uleb128_size(h.ack as u64)
509    } else {
510        0
511    }
512}
513
514#[inline]
515fn should_compact_reliable_header(h: ReliableHeader) -> bool {
516    reliable_compact_size(h) < RELIABLE_HEADER_BYTES
517}
518
519#[inline]
520fn reliable_wire_size(h: ReliableHeader, compact: bool) -> usize {
521    if compact {
522        reliable_compact_size(h)
523    } else {
524        RELIABLE_HEADER_BYTES
525    }
526}
527
528pub(crate) fn write_reliable_header_encoded(h: ReliableHeader, compact: bool, out: &mut Vec<u8>) {
529    if !compact {
530        write_reliable_header(h, out);
531        return;
532    }
533
534    let seq_present = (h.flags & RELIABLE_FLAG_ACK_ONLY) == 0 || h.seq != 0;
535    let ack_present = h.ack != 0 || (h.flags & RELIABLE_FLAG_ACK_ONLY) != 0;
536    let mut wire_flags = h.flags & RELIABLE_PUBLIC_FLAGS_MASK;
537    if seq_present {
538        wire_flags |= RELIABLE_WIRE_FLAG_SEQ_PRESENT;
539    }
540    if ack_present {
541        wire_flags |= RELIABLE_WIRE_FLAG_ACK_PRESENT;
542    }
543    out.push(wire_flags);
544    if seq_present {
545        write_uleb128(h.seq as u64, out);
546    }
547    if ack_present {
548        write_uleb128(h.ack as u64, out);
549    }
550}
551
552#[inline]
553fn read_reliable_header(r: &mut ByteReader) -> Result<ReliableHeader, TelemetryError> {
554    let flags = r.read_bytes(1)?[0];
555    let seq = read_u32_le(r)?;
556    let ack = read_u32_le(r)?;
557    Ok(ReliableHeader { flags, seq, ack })
558}
559
560fn read_reliable_header_compact(r: &mut ByteReader) -> Result<ReliableHeader, TelemetryError> {
561    let wire_flags = r.read_bytes(1)?[0];
562    let seq = if (wire_flags & RELIABLE_WIRE_FLAG_SEQ_PRESENT) != 0 {
563        u32::try_from(read_uleb128(r)?)
564            .map_err(|_| TelemetryError::Unpack("reliable seq too large"))?
565    } else {
566        0
567    };
568    let ack = if (wire_flags & RELIABLE_WIRE_FLAG_ACK_PRESENT) != 0 {
569        u32::try_from(read_uleb128(r)?)
570            .map_err(|_| TelemetryError::Unpack("reliable ack too large"))?
571    } else {
572        0
573    };
574    Ok(ReliableHeader {
575        flags: wire_flags & RELIABLE_PUBLIC_FLAGS_MASK,
576        seq,
577        ack,
578    })
579}
580
581#[inline]
582fn read_reliable_header_encoded(
583    r: &mut ByteReader,
584    compact: bool,
585) -> Result<ReliableHeader, TelemetryError> {
586    if compact {
587        read_reliable_header_compact(r)
588    } else {
589        read_reliable_header(r)
590    }
591}
592
593#[cfg(feature = "std")]
594static ADDRESS_BOOK: OnceLock<Mutex<BTreeMap<u32, Arc<str>>>> = OnceLock::new();
595
596#[cfg(feature = "std")]
597fn address_book() -> &'static Mutex<BTreeMap<u32, Arc<str>>> {
598    ADDRESS_BOOK.get_or_init(|| Mutex::new(BTreeMap::new()))
599}
600
601#[inline]
602pub(crate) fn source_address_for_sender(sender: &str) -> u32 {
603    let addr = sender_address_u32(sender);
604    remember_source_address(addr, sender);
605    addr
606}
607
608#[cfg(feature = "std")]
609pub(crate) fn remember_source_address(addr: u32, sender: &str) {
610    address_book()
611        .lock()
612        .expect("wire address book poisoned")
613        .entry(addr)
614        .or_insert_with(|| Arc::<str>::from(sender));
615}
616
617#[cfg(not(feature = "std"))]
618pub(crate) fn remember_source_address(_addr: u32, _sender: &str) {}
619
620#[cfg(feature = "std")]
621fn sender_name_for_address(addr: u32) -> Arc<str> {
622    address_book()
623        .lock()
624        .expect("wire address book poisoned")
625        .get(&addr)
626        .cloned()
627        .unwrap_or_else(|| format_sender_address(addr))
628}
629
630#[cfg(not(feature = "std"))]
631fn sender_name_for_address(addr: u32) -> Arc<str> {
632    format_sender_address(addr)
633}
634
635fn format_sender_address(addr: u32) -> Arc<str> {
636    // Format the numeric alias on the stack; only the retained Arc allocates.
637    let mut bytes = [0u8; 16];
638    let mut start = bytes.len();
639    let mut remaining = addr;
640    loop {
641        start -= 1;
642        bytes[start] = b'0' + (remaining % 10) as u8;
643        remaining /= 10;
644        if remaining == 0 {
645            break;
646        }
647    }
648    start -= 6;
649    bytes[start..start + 6].copy_from_slice(b"@addr:");
650    Arc::from(core::str::from_utf8(&bytes[start..]).expect("numeric alias is ASCII"))
651}
652
653// ===========================================================================
654// Endpoint bitmap constants and helpers
655// ===========================================================================
656
657/// Number of bits needed to cover all possible `DataEndpoint` discriminants.
658const EP_BITMAP_BITS: usize = 256;
659#[cfg(test)]
660use crate::MAX_VALUE_DATA_ENDPOINT;
661
662/// Number of bytes required to store [`EP_BITMAP_BITS`] bits.
663const EP_BITMAP_BYTES: usize = EP_BITMAP_BITS.div_ceil(8);
664
665/// Build a compact endpoint bitmap from the provided list of endpoints.
666///
667/// Each endpoint `ep` sets the bit at position `ep.as_u32()` in the bitmap.
668/// Bits are packed LSB-first within each byte.
669#[inline]
670fn build_endpoint_bitmap(eps: &[DataEndpoint]) -> [u8; EP_BITMAP_BYTES] {
671    let mut bm = [0u8; EP_BITMAP_BYTES];
672    for &ep in eps {
673        let idx = ep.as_u32() as usize;
674        debug_assert!(idx < EP_BITMAP_BITS, "endpoint discriminant out of range");
675        if idx < EP_BITMAP_BITS {
676            let byte = idx / 8;
677            let bit = idx % 8;
678            bm[byte] |= 1u8 << bit;
679        }
680    }
681    bm
682}
683
684/// Expand a bitmap into registered endpoints in ascending discriminant order.
685fn expand_endpoint_bitmap(bm: &[u8]) -> TelemetryResult<Arc<[DataEndpoint]>> {
686    if bm.len() != EP_BITMAP_BYTES {
687        return Err(TelemetryError::Unpack("bad endpoint bitmap size"));
688    }
689    // Only visit set bits. Sparse telemetry destinations should not initialize
690    // and copy a 256-element endpoint array on every header inspection.
691    let count = bitmap_popcount(bm);
692    let mut endpoints = Arc::<[DataEndpoint]>::new_uninit_slice(count);
693    let slots = Arc::get_mut(&mut endpoints).expect("new endpoint storage is unique");
694    let mut written = 0;
695    for (byte_index, &byte) in bm.iter().enumerate() {
696        let mut bits = byte;
697        while bits != 0 {
698            let idx = byte_index * 8 + bits.trailing_zeros() as usize;
699            slots[written].write(
700                DataEndpoint::try_from_u32(idx as u32)
701                    .ok_or(TelemetryError::Unpack("bad endpoint bit set"))?,
702            );
703            written += 1;
704            bits &= bits - 1;
705        }
706    }
707    // Every set bit initializes exactly one slot; errors drop uninitialized storage.
708    debug_assert_eq!(written, count);
709    Ok(unsafe { endpoints.assume_init() })
710}
711
712pub(crate) fn unique_sorted_endpoints(
713    endpoints: &[DataEndpoint],
714) -> impl Iterator<Item = DataEndpoint> + '_ {
715    let mut previous = None;
716    core::iter::from_fn(move || {
717        let next = endpoints
718            .iter()
719            .copied()
720            .filter(|endpoint| previous.is_none_or(|previous| *endpoint > previous))
721            .min();
722        // Retain the last maximum after exhaustion; subsequent calls remain
723        // exhausted without a separate state field or restarting the sequence.
724        previous = next.or(previous);
725        next
726    })
727}
728
729fn endpoint_arc_from_iter(
730    count: usize,
731    endpoints: impl Iterator<Item = DataEndpoint>,
732) -> TelemetryResult<Arc<[DataEndpoint]>> {
733    let mut output = Arc::<[DataEndpoint]>::new_uninit_slice(count);
734    let slots = Arc::get_mut(&mut output).unwrap();
735    let mut written = 0;
736    for endpoint in endpoints {
737        if written == count {
738            return Err(TelemetryError::Unpack("endpoint count mismatch"));
739        }
740        slots[written].write(endpoint);
741        written += 1;
742    }
743    if written != count {
744        return Err(TelemetryError::Unpack("endpoint count mismatch"));
745    }
746    // SAFETY: every slot was initialized exactly once above.
747    Ok(unsafe { output.assume_init() })
748}
749fn endpoint_list_size(eps: &[DataEndpoint]) -> usize {
750    unique_sorted_endpoints(eps)
751        .map(|ep| uleb128_size(u64::from(ep.0)))
752        .sum()
753}
754fn explicit_endpoint_list(eps: &[DataEndpoint]) -> bool {
755    eps.iter().any(|ep| ep.0 >= EP_BITMAP_BITS as u32) || endpoint_list_size(eps) < EP_BITMAP_BYTES
756}
757fn endpoints_match_schema(ty: DataType, eps: &[DataEndpoint]) -> bool {
758    let meta = message_meta(ty);
759    unique_sorted_endpoints(eps).eq(unique_sorted_endpoints(meta.endpoints_ref()))
760}
761fn read_endpoint_list<'a>(r: &mut ByteReader<'a>, count: usize) -> TelemetryResult<&'a [u8]> {
762    let start = r.off;
763    let mut previous = None;
764    for _ in 0..count {
765        let id = u32::try_from(read_uleb128(r)?)
766            .map_err(|_| TelemetryError::Unpack("endpoint too large"))?;
767        if previous.is_some_and(|previous| id <= previous) {
768            return Err(TelemetryError::Unpack(
769                "endpoint list is not unique and sorted",
770            ));
771        }
772        previous = Some(id);
773    }
774    Ok(&r.buf[start..r.off])
775}
776fn endpoint_list_iter(bytes: &[u8]) -> impl Iterator<Item = DataEndpoint> + '_ {
777    let mut r = ByteReader::new(bytes);
778    core::iter::from_fn(move || {
779        if r.remaining() == 0 {
780            return None;
781        }
782        read_uleb128(&mut r)
783            .ok()
784            .and_then(|id| u32::try_from(id).ok())
785            .map(DataEndpoint)
786    })
787}
788
789#[inline]
790fn schema_endpoints_from_type(ty: DataType, nep: usize) -> TelemetryResult<Arc<[DataEndpoint]>> {
791    // Match bitmap ordering and duplicate removal without expanding all 256
792    // possible destinations for the usual one-endpoint schema.
793    let meta = message_meta(ty);
794    let schema = meta.endpoints_ref();
795    if schema.len() == nep
796        && schema
797            .windows(2)
798            .all(|pair| pair[0].as_u32() < pair[1].as_u32())
799        && schema
800            .iter()
801            .all(|ep| DataEndpoint::try_from_u32(ep.as_u32()).is_some())
802    {
803        // Compiled/registered metadata already owns sorted endpoints on hosts;
804        // embedded metadata is static. Avoid a temporary Vec in either case.
805        #[cfg(feature = "std")]
806        return Ok(meta.endpoints);
807        #[cfg(not(feature = "std"))]
808        return Ok(Arc::from(schema));
809    }
810    endpoint_arc_from_iter(nep, unique_sorted_endpoints(schema))
811}
812
813#[inline]
814fn endpoints_from_wire_or_schema(
815    r: &mut ByteReader<'_>,
816    bitmap_present: bool,
817    list_present: bool,
818    ty: Option<DataType>,
819    nep: usize,
820) -> TelemetryResult<Arc<[DataEndpoint]>> {
821    if list_present {
822        if bitmap_present {
823            return Err(TelemetryError::Unpack("ambiguous endpoint encoding"));
824        }
825        let bytes = read_endpoint_list(r, nep)?;
826        #[cfg(feature = "std")]
827        if let Some(meta) = ty.and_then(crate::config::try_get_message_meta)
828            && endpoint_list_iter(bytes).eq(meta.endpoints.iter().copied())
829        {
830            return Ok(meta.endpoints);
831        }
832        return endpoint_arc_from_iter(nep, endpoint_list_iter(bytes));
833    }
834    if bitmap_present {
835        let bm = r.read_bytes(EP_BITMAP_BYTES)?;
836        let endpoints = expand_endpoint_bitmap(bm)?;
837        if endpoints.len() != nep {
838            return Err(TelemetryError::Unpack("endpoint count mismatch"));
839        }
840        Ok(endpoints)
841    } else {
842        let ty = ty.ok_or(TelemetryError::InvalidType)?;
843        schema_endpoints_from_type(ty, nep)
844    }
845}
846
847#[inline]
848fn data_type_id_from_wire(ty_v: u64) -> TelemetryResult<u32> {
849    let ty_u32 = u32::try_from(ty_v).map_err(|_| TelemetryError::Unpack("type too large"))?;
850    Ok(ty_u32)
851}
852
853// ===========================================================================
854// Packing
855// ===========================================================================
856
857/// Pack a [`Packet`] into the compact v2 wire format.
858///
859/// The returned `Arc<[u8]>` owns the packed bytes and can be shared cheaply.
860/// # Arguments
861/// - `pkt`: Telemetry packet to pack.
862///
863/// # Returns
864/// - `Arc<[u8]>`: Packed packet in compact v2 wire format.
865pub fn pack_packet(pkt: &Packet) -> Arc<[u8]> {
866    if is_reliable_type(pkt.data_type()) {
867        // Default to an unsequenced reliable header to keep the wire format consistent.
868        // Use `pack_packet_with_reliable` for ordered/retransmitted delivery.
869        let hdr = ReliableHeader {
870            flags: RELIABLE_FLAG_UNSEQUENCED,
871            seq: 0,
872            ack: 0,
873        };
874        return pack_packet_with_reliable(pkt, hdr);
875    }
876    pack_packet_inner(pkt, None)
877}
878
879/// Pack a [`Packet`] with an explicit reliable header.
880///
881/// This should be used for data types configured as `reliable` in the schema.
882pub fn pack_packet_with_reliable(pkt: &Packet, header: ReliableHeader) -> Arc<[u8]> {
883    pack_packet_inner(pkt, Some(header))
884}
885
886/// Pack a reliable ACK-only control frame for the given data type.
887///
888/// The resulting bytes are not a valid `Packet` and should be handled
889/// by the router's reliable layer.
890pub fn pack_reliable_ack(sender: &str, ty: DataType, timestamp_ms: u64, ack: u32) -> Arc<[u8]> {
891    let source_address = source_address_for_sender(sender);
892
893    // No payload for ACK-only control frames.
894    let mut out = Vec::with_capacity(32 + EP_BITMAP_BYTES + CRC32_BYTES);
895
896    let flags: u8 = FLAG_ENDPOINT_LIST_PRESENT | FLAG_RELIABLE_HEADER_PRESENT;
897    out.push(flags);
898    out.push(0u8); // NEP = 0
899
900    write_uleb128(ty.as_u32() as u64, &mut out);
901    write_uleb128(0u64, &mut out); // payload size
902    write_uleb128(timestamp_ms, &mut out);
903    write_uleb128(source_address as u64, &mut out);
904
905    let reliable = ReliableHeader {
906        flags: RELIABLE_FLAG_ACK_ONLY,
907        seq: 0,
908        ack,
909    };
910    if should_compact_reliable_header(reliable) {
911        out[0] |= FLAG_COMPACT_RELIABLE_HEADER;
912        write_reliable_header_encoded(reliable, true, &mut out);
913    } else {
914        write_reliable_header_encoded(reliable, false, &mut out);
915    }
916    append_crc32(&mut out);
917
918    Arc::<[u8]>::from(out)
919}
920
921fn pack_packet_inner(pkt: &Packet, reliable: Option<ReliableHeader>) -> Arc<[u8]> {
922    pack_packet_inner_with_contract(
923        pkt,
924        reliable,
925        pkt.wire_shape(),
926        pkt.wire_target_senders(),
927        None,
928    )
929    .expect("plaintext packet packing failed")
930}
931
932/// Pack `pkt` while explicitly controlling the wire-contract metadata.
933///
934/// Router and relay forwarding paths use this helper when they need to attach
935/// or preserve a migration-safe contract instead of simply serializing the
936/// packet against the current runtime schema/topology view.
937///
938/// # Parameters
939/// - `pkt`: Logical packet to pack.
940/// - `reliable`: Optional hop-level reliable header to append after the
941///   contract.
942/// - `shape`: Optional inline payload shape to encode into the contract so
943///   downstream unpackers can validate/decode the payload against the
944///   original shape.
945/// - `target_senders`: Frozen destination-holder sender hashes that keep
946///   in-flight routing bound to the intended holders.
947///
948/// # Returns
949/// - `Ok(Arc<[u8]>)` containing the packed frame bytes.
950/// - `Err(TelemetryError)` if contract encoding fails.
951pub(crate) fn pack_packet_with_wire_contract(
952    pkt: &Packet,
953    reliable: Option<ReliableHeader>,
954    shape: Option<MessageElement>,
955    target_senders: &[u64],
956) -> TelemetryResult<Arc<[u8]>> {
957    pack_packet_inner_with_contract(pkt, reliable, shape, target_senders, None)
958}
959
960#[derive(Clone, Copy, Debug)]
961#[cfg_attr(not(feature = "cryptography"), allow(dead_code))]
962pub(crate) struct E2eSealConfig {
963    pub key_id: u32,
964}
965
966#[cfg(feature = "cryptography")]
967pub(crate) fn pack_packet_with_wire_contract_e2e(
968    pkt: &Packet,
969    reliable: Option<ReliableHeader>,
970    shape: Option<MessageElement>,
971    target_senders: &[u64],
972    e2e: E2eSealConfig,
973) -> TelemetryResult<Arc<[u8]>> {
974    pack_packet_inner_with_contract(pkt, reliable, shape, target_senders, Some(e2e))
975}
976
977fn pack_packet_inner_with_contract(
978    pkt: &Packet,
979    reliable: Option<ReliableHeader>,
980    shape: Option<MessageElement>,
981    target_senders: &[u64],
982    e2e: Option<E2eSealConfig>,
983) -> TelemetryResult<Arc<[u8]>> {
984    with_packet_frame_parts(
985        pkt,
986        reliable,
987        shape,
988        target_senders,
989        e2e,
990        |header, payload| Ok(join_frame_with_crc(header, payload)),
991    )
992}
993
994/// Serialize the normal plaintext header while retaining the existing payload.
995/// Used by chunked internal discovery to avoid a second schema-sized buffer.
996pub(crate) fn with_plain_packet_parts<T>(
997    pkt: &Packet,
998    consume: impl FnOnce(&[u8], &[u8]) -> TelemetryResult<T>,
999) -> TelemetryResult<T> {
1000    let reliable = is_reliable_type(pkt.data_type()).then_some(ReliableHeader {
1001        flags: RELIABLE_FLAG_UNSEQUENCED,
1002        seq: 0,
1003        ack: 0,
1004    });
1005    with_packet_frame_parts(
1006        pkt,
1007        reliable,
1008        pkt.wire_shape(),
1009        pkt.wire_target_senders(),
1010        None,
1011        consume,
1012    )
1013}
1014
1015fn packet_endpoint_bitmap_present(
1016    pkt: &Packet,
1017    shape: Option<MessageElement>,
1018    target_senders: &[u64],
1019) -> bool {
1020    let carries_wire_contract = shape.is_some() || !target_senders.is_empty();
1021    let endpoints_are_schema_default = endpoints_match_schema(pkt.data_type(), pkt.endpoints());
1022    // Reliability controls have a protocol-defined endpoint on every peer;
1023    // their receipt contract does not require a second, 32-byte copy of it.
1024    // Runtime application types retain explicit endpoints with a frozen
1025    // contract, since their schemas may differ during discovery/rejoin.
1026    let fixed_control_endpoints = matches!(
1027        pkt.data_type(),
1028        DataType::ReliableAck | DataType::ReliablePartialAck | DataType::ReliablePacketRequest
1029    );
1030    // Application catalogs may be complementary or still converging. A short
1031    // explicit list preserves this packet's actual destinations in that case.
1032    pkt.data_type().0 > DataType::P2pMessage.0
1033        || !endpoints_are_schema_default
1034        || (carries_wire_contract && !fixed_control_endpoints)
1035}
1036
1037fn with_packet_frame_parts<T>(
1038    pkt: &Packet,
1039    reliable: Option<ReliableHeader>,
1040    shape: Option<MessageElement>,
1041    target_senders: &[u64],
1042    #[cfg_attr(not(feature = "cryptography"), allow(unused_variables))] e2e: Option<E2eSealConfig>,
1043    consume: impl FnOnce(&[u8], &[u8]) -> TelemetryResult<T>,
1044) -> TelemetryResult<T> {
1045    let carries_wire_contract = shape.is_some() || !target_senders.is_empty();
1046    let endpoint_bitmap_present = packet_endpoint_bitmap_present(pkt, shape, target_senders);
1047    let endpoint_list_present = endpoint_bitmap_present && explicit_endpoint_list(pkt.endpoints());
1048    let nep_unique = unique_sorted_endpoints(pkt.endpoints()).count();
1049    let endpoint_bytes = if endpoint_list_present {
1050        endpoint_list_size(pkt.endpoints())
1051    } else if endpoint_bitmap_present {
1052        EP_BITMAP_BYTES
1053    } else {
1054        0
1055    };
1056
1057    let source_address = source_address_for_sender(pkt.sender());
1058
1059    // Decide whether to compress the payload.
1060    let payload = pkt.payload();
1061    let (payload_compressed, payload_wire) = payload_compression::compress_if_beneficial(payload);
1062
1063    // Size the variable-length prefix exactly. A 16-byte estimate can be
1064    // too small for real timestamps/nonces, doubling a multi-KiB schema
1065    // buffer while its payload and the old allocation are still retained.
1066    let reliable_is_compact = reliable.is_some_and(should_compact_reliable_header);
1067    let reliable_len = if let Some(hdr) = reliable {
1068        reliable_wire_size(hdr, reliable_is_compact)
1069    } else {
1070        0
1071    };
1072    let contract_bytes = wire_contract_size(shape, target_senders);
1073    let contract_len = if carries_wire_contract {
1074        uleb128_size(contract_bytes as u64) + contract_bytes
1075    } else {
1076        0
1077    };
1078    let capacity = header_size_bytes(pkt)
1079        + endpoint_bytes
1080        + contract_len
1081        + reliable_len
1082        + payload_wire.len()
1083        + CRC32_BYTES;
1084    #[cfg(feature = "cryptography")]
1085    let capacity = capacity
1086        + e2e.map_or(0, |seal| {
1087            uleb128_size(u64::from(seal.key_id))
1088                + uleb128_size(payload_wire.len() as u64)
1089                + uleb128_size(E2E_NONCE_LEN as u64)
1090                + E2E_NONCE_LEN
1091                + uleb128_size(E2E_TAG_CAP as u64)
1092                + E2E_TAG_CAP
1093        });
1094    // Plain frames need only a small header scratch buffer. Construct the
1095    // final Arc directly: Vec -> Arc otherwise retains two frame-sized
1096    // allocations alongside the schema payload on embedded routers.
1097    #[cfg(feature = "cryptography")]
1098    let encrypted = e2e.is_some();
1099    #[cfg(not(feature = "cryptography"))]
1100    let encrypted = false;
1101    let scratch_capacity = if encrypted {
1102        capacity
1103    } else {
1104        capacity - payload_wire.len() - CRC32_BYTES
1105    };
1106    let mut out = Vec::with_capacity(scratch_capacity);
1107    #[cfg(debug_assertions)]
1108    let initial_capacity = out.capacity();
1109
1110    // FLAGS byte
1111    let mut flags: u8 = 0;
1112    if payload_compressed {
1113        flags |= FLAG_COMPRESSED_PAYLOAD;
1114    }
1115    if carries_wire_contract {
1116        flags |= FLAG_WIRE_CONTRACT;
1117    }
1118    if pkt.nonce() != 0 {
1119        flags |= FLAG_PACKET_NONCE;
1120    }
1121    if endpoint_list_present {
1122        flags |= FLAG_ENDPOINT_LIST_PRESENT;
1123    } else if endpoint_bitmap_present {
1124        flags |= FLAG_ENDPOINT_BITMAP_PRESENT;
1125    }
1126    if reliable.is_some() {
1127        flags |= FLAG_RELIABLE_HEADER_PRESENT;
1128    }
1129    if reliable_is_compact {
1130        flags |= FLAG_COMPACT_RELIABLE_HEADER;
1131    }
1132    #[cfg(feature = "cryptography")]
1133    if e2e.is_some() {
1134        flags |= FLAG_E2E_ENCRYPTED_PAYLOAD;
1135    }
1136    out.push(flags);
1137
1138    assert!(
1139        nep_unique <= u8::MAX as usize,
1140        "too many endpoints selected to fit in NEP u8"
1141    );
1142    out.push(nep_unique as u8);
1143
1144    // NOTE: data_size is the *logical* (uncompressed) payload size.
1145    write_uleb128(pkt.data_type().as_u32() as u64, &mut out);
1146    write_uleb128(pkt.data_size() as u64, &mut out);
1147    write_uleb128(pkt.timestamp(), &mut out);
1148    if pkt.nonce() != 0 {
1149        write_uleb128(pkt.nonce() as u64, &mut out);
1150    }
1151
1152    write_uleb128(source_address as u64, &mut out);
1153
1154    if endpoint_list_present {
1155        for endpoint in unique_sorted_endpoints(pkt.endpoints()) {
1156            write_uleb128(u64::from(endpoint.0), &mut out);
1157        }
1158    } else if endpoint_bitmap_present {
1159        out.extend_from_slice(&build_endpoint_bitmap(pkt.endpoints()));
1160    }
1161    if (flags & FLAG_WIRE_CONTRACT) != 0 {
1162        // The contract must appear before the reliable header because it may
1163        // carry the "reliable header present" bit for packets whose current
1164        // runtime schema lookup no longer reflects the original wire semantics.
1165        write_uleb128(contract_bytes as u64, &mut out);
1166        write_wire_contract(shape, target_senders, reliable.is_some(), &mut out)?;
1167    }
1168    if let Some(hdr) = reliable {
1169        write_reliable_header_encoded(hdr, reliable_is_compact, &mut out);
1170    }
1171    #[cfg(feature = "cryptography")]
1172    if let Some(e2e) = e2e {
1173        write_encrypted_payload(pkt, e2e.key_id, &payload_wire, &mut out)?;
1174        return consume(&out, &[]);
1175    }
1176    #[cfg(debug_assertions)]
1177    debug_assert_eq!(
1178        out.capacity(),
1179        initial_capacity,
1180        "packet header grew its buffer"
1181    );
1182    consume(&out, &payload_wire)
1183}
1184
1185// ===========================================================================
1186// Unpacking (full packet)
1187// ===========================================================================
1188
1189/// Unpack a full [`Packet`] from a packed v2 wire frame.
1190///
1191/// This validates the frame CRC, resolves the endpoint set, decodes the
1192/// optional migration-safe wire contract, parses the optional reliable header,
1193/// and reconstructs the logical `Packet`. When the frame carries an inline wire
1194/// shape, the returned packet preserves that shape so payload validation and
1195/// formatting remain stable even if the local runtime schema has changed since
1196/// the packet was originally packed.
1197///
1198/// # Parameters
1199/// - `buf`: Complete packed frame bytes, including the CRC32 trailer.
1200///
1201/// # Returns
1202/// - `Ok(Packet)` when the frame is well-formed and contains a payload-bearing
1203///   telemetry packet.
1204///
1205/// # Errors
1206/// - [`TelemetryError::Unpack`] if the frame is malformed, truncated, or
1207///   fails CRC validation.
1208/// - [`TelemetryError::InvalidType`] if the type ID is not valid and the frame
1209///   does not carry enough inline shape information to keep decoding it.
1210pub fn unpack_packet(buf: &[u8]) -> Result<Packet, TelemetryError> {
1211    unpack_packet_with_role(buf, false)
1212}
1213
1214pub(crate) fn unpack_packet_with_role(buf: &[u8], managed: bool) -> Result<Packet, TelemetryError> {
1215    let data = verify_crc32(buf)?;
1216    if data.is_empty() {
1217        return Err(TelemetryError::Unpack("short prelude"));
1218    }
1219    let mut r = ByteReader::new(data);
1220
1221    let flags = r.read_bytes(1)?[0];
1222    let payload_is_compressed = (flags & FLAG_COMPRESSED_PAYLOAD) != 0;
1223    let endpoint_bitmap_present = (flags & FLAG_ENDPOINT_BITMAP_PRESENT) != 0;
1224    let compact_reliable_header = (flags & FLAG_COMPACT_RELIABLE_HEADER) != 0;
1225    #[cfg(feature = "cryptography")]
1226    let payload_is_encrypted = (flags & FLAG_E2E_ENCRYPTED_PAYLOAD) != 0;
1227    #[cfg(not(feature = "cryptography"))]
1228    if (flags & 0x10) != 0 {
1229        return Err(TelemetryError::Unpack("e2e crypto unsupported"));
1230    }
1231
1232    let nep = r.read_bytes(1)?[0] as usize;
1233
1234    let ty_v = read_uleb128(&mut r)?;
1235    let dsz = read_uleb128(&mut r)? as usize; // logical (uncompressed) payload size
1236    let ts_v = read_uleb128(&mut r)?;
1237    let nonce = if (flags & FLAG_PACKET_NONCE) != 0 {
1238        u16::try_from(read_uleb128(&mut r)?)
1239            .map_err(|_| TelemetryError::Unpack("packet nonce too large"))?
1240    } else {
1241        0
1242    };
1243    let source_address = u32::try_from(read_uleb128(&mut r)?)
1244        .map_err(|_| TelemetryError::Unpack("source address too large"))?;
1245    let sender_str = sender_name_for_address(source_address);
1246    let ty_u32 = data_type_id_from_wire(ty_v)?;
1247    let known_ty = DataType::try_from_u32(ty_u32);
1248    let endpoint_bytes = if endpoint_bitmap_present {
1249        EP_BITMAP_BYTES
1250    } else {
1251        0
1252    };
1253
1254    // For uncompressed payload: bitmap + [contract] + [reliable] + payload(dsz)
1255    // For compressed payload: bitmap + [contract] + [reliable] + at least 1 byte.
1256    if !payload_is_compressed {
1257        if r.remaining() < endpoint_bytes + dsz {
1258            return Err(TelemetryError::Unpack("short buffer"));
1259        }
1260    } else if r.remaining() < endpoint_bytes + 1 {
1261        return Err(TelemetryError::Unpack("short buffer"));
1262    }
1263
1264    let eps = endpoints_from_wire_or_schema(
1265        &mut r,
1266        endpoint_bitmap_present,
1267        flags & FLAG_ENDPOINT_LIST_PRESENT != 0,
1268        known_ty,
1269        nep,
1270    )?;
1271
1272    let contract = decode_wire_contract(&mut r, (flags & FLAG_WIRE_CONTRACT) != 0)?;
1273    let ty = known_ty
1274        .or_else(|| contract.shape.map(|_| DataType(ty_u32)))
1275        .ok_or(TelemetryError::InvalidType)?;
1276
1277    // ----- Reliable header (optional) -----
1278    let mut reliable_hdr: Option<ReliableHeader> = None;
1279    if flags & FLAG_RELIABLE_HEADER_PRESENT != 0 || contract.has_reliable_header {
1280        let hdr = read_reliable_header_encoded(&mut r, compact_reliable_header)?;
1281        if (hdr.flags & RELIABLE_FLAG_ACK_ONLY) != 0 {
1282            return Err(TelemetryError::Unpack("reliable control frame"));
1283        }
1284        reliable_hdr = Some(hdr);
1285    }
1286
1287    // Reassembled side frames bypass the small-fragment ingress estimate.
1288    // Check again immediately before allocating their logical payload, including
1289    // decompression/decryption scratch when enabled.
1290    let payload_copies = if payload_is_compressed { 3 } else { 1 };
1291    #[cfg(feature = "cryptography")]
1292    let payload_copies = payload_copies + usize::from(payload_is_encrypted);
1293    let small_ack = matches!(ty, DataType::ReliableAck | DataType::ReliablePartialAck)
1294        && dsz <= 32
1295        && payload_copies == 1;
1296    if crate::memory_admission::supports_work_kinds()
1297        && !small_ack
1298        && buf.len() <= 128
1299        && flags & 0x11 == 0
1300        && (crate::memory_admission::protected_discovery_request_shape(
1301            ty,
1302            dsz == 0 && r.remaining() == 0,
1303            eps.as_ref() == [DataEndpoint::Discovery],
1304            contract
1305                .target_senders
1306                .as_ref()
1307                .map_or(0, |owners| owners.len()),
1308            contract.shape,
1309        ) || crate::memory_admission::protected_control_shape(
1310            ty,
1311            nep,
1312            contract
1313                .target_senders
1314                .as_ref()
1315                .map_or(0, |owners| owners.len()),
1316            contract.shape,
1317            reliable_hdr.is_some(),
1318            managed,
1319        ))
1320    {
1321        crate::memory_admission::check_small_protected_decode(buf.len())?;
1322    } else {
1323        crate::memory_admission::check(
1324            if small_ack {
1325                512
1326            } else {
1327                dsz.saturating_mul(payload_copies).saturating_add(512)
1328            },
1329            dsz.saturating_add(32),
1330        )?;
1331    }
1332    // ----- Payload handling -----
1333    let payload: StandardSmallPayload = {
1334        #[cfg(feature = "cryptography")]
1335        let payload_wire_owned;
1336        #[cfg(feature = "cryptography")]
1337        let payload_wire: &[u8] = if payload_is_encrypted {
1338            let aad_end = r.off;
1339            payload_wire_owned = read_encrypted_payload(&mut r, &data[..aad_end], dsz)?;
1340            &payload_wire_owned
1341        } else if !payload_is_compressed {
1342            r.read_bytes(dsz)?
1343        } else {
1344            let comp_len = r.remaining();
1345            r.read_bytes(comp_len)?
1346        };
1347
1348        #[cfg(not(feature = "cryptography"))]
1349        let payload_wire: &[u8] = if !payload_is_compressed {
1350            r.read_bytes(dsz)?
1351        } else {
1352            let comp_len = r.remaining();
1353            r.read_bytes(comp_len)?
1354        };
1355
1356        if payload_is_compressed {
1357            let decompressed = payload_compression::decompress(payload_wire, dsz)?;
1358            crate::memory_admission::check(dsz.saturating_add(512), dsz.saturating_add(32))?;
1359            StandardSmallPayload::new(&decompressed)
1360        } else {
1361            if payload_wire.len() != dsz {
1362                return Err(TelemetryError::Unpack("payload length mismatch"));
1363            }
1364            StandardSmallPayload::new(payload_wire)
1365        }
1366    };
1367
1368    // `Packet` preserves logical payload data plus wire-contract metadata, but
1369    // not hop-level reliable transport state. The router/relay reliable layer
1370    // consumes that header before handing the logical packet onward.
1371    let _ = reliable_hdr;
1372    Packet::new_with_owned_wire_contract(
1373        ty,
1374        eps,
1375        sender_str,
1376        ts_v,
1377        nonce,
1378        payload,
1379        contract.shape,
1380        contract.target_senders,
1381    )
1382}
1383
1384// ===========================================================================
1385// Peek / envelope-only decode
1386// ===========================================================================
1387
1388/// Decode only the routing-relevant envelope of a packed packet.
1389///
1390/// This reads enough of the frame to expose type, endpoints, sender, timestamp,
1391/// and any migration-safe contract metadata without touching payload bytes.
1392/// That makes it the fast path for routing and other header-only inspection.
1393///
1394/// # Parameters
1395/// - `buf`: Complete packed frame bytes, including the CRC32 trailer.
1396///
1397/// # Returns
1398/// - `Ok(TelemetryEnvelope)` containing the logical packet envelope plus any
1399///   inline wire-shape and target-sender metadata.
1400///
1401/// # Errors
1402/// - [`TelemetryError::Unpack`] if the frame is malformed or fails CRC.
1403/// - [`TelemetryError::InvalidType`] if the type ID cannot be resolved.
1404pub fn peek_envelope(buf: &[u8]) -> TelemetryResult<TelemetryEnvelope> {
1405    Ok(peek_routing_envelope(buf)?.into_owned())
1406}
1407
1408/// Validated routing metadata without owned strings or heap-backed arrays.
1409/// The fixed bitmap is normalized against the current schema on every parse;
1410/// nothing is cached across schema updates or packet lifetimes.
1411#[derive(Clone, Debug)]
1412pub(crate) struct RoutingEnvelope<'a> {
1413    pub ty: DataType,
1414    bitmap: [u64; EP_BITMAP_BYTES / 8],
1415    endpoint_list: Option<&'a [u8]>,
1416    schema_endpoints: Option<crate::MessageMeta>,
1417    pub source_address: u32,
1418    pub timestamp_ms: u64,
1419    pub wire_shape: Option<MessageElement>,
1420    pub is_reliable: bool,
1421    payload_is_empty: bool,
1422    targets: &'a [u8],
1423    has_reliable_header: bool,
1424}
1425pub(crate) struct RoutingEndpointIter<'a> {
1426    envelope: &'a RoutingEnvelope<'a>,
1427    word: usize,
1428    bits: u64,
1429    list_offset: usize,
1430    previous: Option<DataEndpoint>,
1431    finished: bool,
1432}
1433impl Iterator for RoutingEndpointIter<'_> {
1434    type Item = DataEndpoint;
1435    #[inline(never)]
1436    fn next(&mut self) -> Option<DataEndpoint> {
1437        if self.finished {
1438            return None;
1439        }
1440        if let Some(bytes) = self.envelope.endpoint_list {
1441            let mut reader = ByteReader {
1442                buf: bytes,
1443                off: self.list_offset,
1444            };
1445            if reader.remaining() == 0 {
1446                self.finished = true;
1447                return None;
1448            }
1449            let endpoint = read_uleb128(&mut reader)
1450                .ok()
1451                .and_then(|id| u32::try_from(id).ok())
1452                .map(DataEndpoint);
1453            self.list_offset = reader.off;
1454            return endpoint;
1455        }
1456        if let Some(meta) = &self.envelope.schema_endpoints {
1457            let next = meta
1458                .endpoints_ref()
1459                .iter()
1460                .copied()
1461                .filter(|ep| self.previous.is_none_or(|previous| *ep > previous))
1462                .min();
1463            self.previous = next;
1464            self.finished = next.is_none();
1465            return next;
1466        }
1467        while self.bits == 0 {
1468            self.word += 1;
1469            if self.word == self.envelope.bitmap.len() {
1470                self.finished = true;
1471                return None;
1472            }
1473            self.bits = self.envelope.bitmap[self.word];
1474        }
1475        let bit = self.bits.trailing_zeros();
1476        self.bits &= self.bits - 1;
1477        Some(DataEndpoint((self.word * 64 + bit as usize) as u32))
1478    }
1479}
1480
1481impl RoutingEnvelope<'_> {
1482    pub fn endpoints(&self) -> RoutingEndpointIter<'_> {
1483        RoutingEndpointIter {
1484            envelope: self,
1485            word: 0,
1486            bits: self.bitmap[0],
1487            list_offset: 0,
1488            previous: None,
1489            finished: false,
1490        }
1491    }
1492    pub fn target_senders(&self) -> impl ExactSizeIterator<Item = u64> + '_ {
1493        self.targets
1494            .as_chunks::<8>()
1495            .0
1496            .iter()
1497            .map(|bytes| u64::from_le_bytes(*bytes))
1498    }
1499    fn into_owned(self) -> TelemetryEnvelope {
1500        TelemetryEnvelope {
1501            ty: self.ty,
1502            endpoints: self.endpoints().collect::<Arc<[_]>>(),
1503            sender: sender_name_for_address(self.source_address),
1504            source_address: self.source_address,
1505            timestamp_ms: self.timestamp_ms,
1506            wire_shape: self.wire_shape,
1507            target_senders: self.target_senders().collect::<Arc<[_]>>(),
1508        }
1509    }
1510}
1511
1512fn read_routing_envelope<'a>(r: &mut ByteReader<'a>) -> TelemetryResult<RoutingEnvelope<'a>> {
1513    let flags = r.read_bytes(1)?[0];
1514    let nep = r.read_bytes(1)?[0] as usize;
1515    let ty_v = read_uleb128(r)?;
1516    let payload_is_empty = read_uleb128(r)? == 0;
1517    let timestamp_ms = read_uleb128(r)?;
1518    if flags & FLAG_PACKET_NONCE != 0 {
1519        let _ = read_uleb128(r)?;
1520    }
1521    let source_address = u32::try_from(read_uleb128(r)?)
1522        .map_err(|_| TelemetryError::Unpack("source address too large"))?;
1523    let ty_u32 = data_type_id_from_wire(ty_v)?;
1524    let metadata = crate::config::try_get_message_meta(DataType(ty_u32));
1525    let known_ty = metadata.as_ref().map(|_| DataType(ty_u32));
1526    let is_reliable = flags & FLAG_RELIABLE_HEADER_PRESENT != 0;
1527    // Word-sized bitmaps avoid repeatedly scanning 32 individual bytes for
1528    // the usual one-destination telemetry frame on size-optimized MCUs.
1529    let mut bitmap = [0u64; EP_BITMAP_BYTES / 8];
1530    let mut endpoint_list = None;
1531    let mut schema_endpoints = None;
1532    if flags & FLAG_ENDPOINT_LIST_PRESENT != 0 {
1533        if flags & FLAG_ENDPOINT_BITMAP_PRESENT != 0 {
1534            return Err(TelemetryError::Unpack("ambiguous endpoint encoding"));
1535        }
1536        let bytes = read_endpoint_list(r, nep)?;
1537        endpoint_list = Some(bytes);
1538    } else if flags & FLAG_ENDPOINT_BITMAP_PRESENT != 0 {
1539        let bytes = r.read_bytes(EP_BITMAP_BYTES)?;
1540        for (word, chunk) in bitmap.iter_mut().zip(bytes.as_chunks::<8>().0) {
1541            *word = u64::from_le_bytes(*chunk);
1542        }
1543        if bitmap
1544            .iter()
1545            .map(|word| word.count_ones() as usize)
1546            .sum::<usize>()
1547            != nep
1548        {
1549            return Err(TelemetryError::Unpack("endpoint count mismatch"));
1550        }
1551        for (index, &word) in bitmap.iter().enumerate() {
1552            let mut bits = word;
1553            while bits != 0 {
1554                let id = index * 64 + bits.trailing_zeros() as usize;
1555                if !crate::config::endpoint_exists(DataEndpoint(id as u32)) {
1556                    return Err(TelemetryError::Unpack("bad endpoint bit set"));
1557                }
1558                bits &= bits - 1;
1559            }
1560        }
1561    } else {
1562        let meta = metadata.ok_or(TelemetryError::InvalidType)?;
1563        if unique_sorted_endpoints(meta.endpoints_ref()).count() != nep {
1564            return Err(TelemetryError::Unpack("endpoint count mismatch"));
1565        }
1566        schema_endpoints = Some(meta);
1567    }
1568    let mut shape = None;
1569    let mut targets: &'a [u8] = &[];
1570    let mut has_reliable_header = false;
1571    if flags & FLAG_WIRE_CONTRACT != 0 {
1572        let len = usize::try_from(read_uleb128(r)?)
1573            .map_err(|_| TelemetryError::Unpack("wire contract length"))?;
1574        let mut cr = ByteReader::new(r.read_bytes(len)?);
1575        let contract_flags = cr.read_bytes(1)?[0];
1576        if contract_flags & CONTRACT_FLAG_SHAPE != 0 {
1577            shape = Some(decode_wire_shape(&mut cr)?);
1578        }
1579        if contract_flags & CONTRACT_FLAG_TARGETS != 0 {
1580            let count = usize::try_from(read_uleb128(&mut cr)?)
1581                .map_err(|_| TelemetryError::Unpack("wire contract target count"))?;
1582            let bytes = count
1583                .checked_mul(8)
1584                .ok_or(TelemetryError::Unpack("wire contract target count"))?;
1585            targets = cr.read_bytes(bytes)?;
1586        }
1587        if cr.remaining() != 0 {
1588            return Err(TelemetryError::Unpack("wire contract trailing bytes"));
1589        }
1590        has_reliable_header = contract_flags & CONTRACT_FLAG_RELIABLE_HEADER != 0;
1591    }
1592    if nep == 0 {
1593        if !is_reliable && !has_reliable_header {
1594            return Err(TelemetryError::EmptyEndpoints);
1595        }
1596        let mut header = ByteReader {
1597            buf: r.buf,
1598            off: r.off,
1599        };
1600        let reliable =
1601            read_reliable_header_encoded(&mut header, flags & FLAG_COMPACT_RELIABLE_HEADER != 0)?;
1602        if reliable.flags & RELIABLE_FLAG_ACK_ONLY == 0 {
1603            return Err(TelemetryError::EmptyEndpoints);
1604        }
1605    }
1606    let ty = known_ty.unwrap_or(DataType(ty_u32));
1607    Ok(RoutingEnvelope {
1608        ty,
1609        bitmap,
1610        endpoint_list,
1611        schema_endpoints,
1612        source_address,
1613        timestamp_ms,
1614        wire_shape: shape,
1615        is_reliable,
1616        payload_is_empty,
1617        targets,
1618        has_reliable_header,
1619    })
1620}
1621
1622/// Read only the bounded type prefix for queue scheduling. This is deliberately
1623/// not frame validation: dispatch must still verify CRC and the full envelope.
1624/// Scanning an RX queue must not checksum every retained payload on each pass.
1625pub(crate) fn scheduling_type(buf: &[u8]) -> Option<DataType> {
1626    let mut reader = ByteReader::new(buf.get(..buf.len().min(12))?);
1627    reader.read_bytes(2).ok()?;
1628    let id = data_type_id_from_wire(read_uleb128(&mut reader).ok()?).ok()?;
1629    DataType::try_from_u32(id)
1630}
1631
1632pub(crate) fn peek_routing_envelope(buf: &[u8]) -> TelemetryResult<RoutingEnvelope<'_>> {
1633    let data = verify_crc32(buf)?;
1634    read_routing_envelope(&mut ByteReader::new(data))
1635}
1636
1637/// Decode the header/envelope and optional reliable header without touching the payload.
1638pub struct TelemetryFrameInfo {
1639    pub envelope: TelemetryEnvelope,
1640    pub reliable: Option<ReliableHeader>,
1641}
1642
1643impl TelemetryFrameInfo {
1644    #[inline]
1645    /// Returns `true` when the frame carries only a reliable-delivery acknowledgment.
1646    pub fn ack_only(&self) -> bool {
1647        self.reliable
1648            .map(|h| (h.flags & RELIABLE_FLAG_ACK_ONLY) != 0)
1649            .unwrap_or(false)
1650    }
1651}
1652
1653pub(crate) struct RoutingFrameInfo<'a> {
1654    pub envelope: RoutingEnvelope<'a>,
1655    pub reliable: Option<ReliableHeader>,
1656    pub empty_payload: bool,
1657}
1658impl RoutingFrameInfo<'_> {
1659    pub fn ack_only(&self) -> bool {
1660        self.reliable
1661            .is_some_and(|header| header.flags & RELIABLE_FLAG_ACK_ONLY != 0)
1662    }
1663}
1664/// Borrow an uncompressed discovery payload without owning its envelope or bytes.
1665/// Sequenced, piggyback-ACK, compressed, targeted and encrypted frames retain normal processing.
1666#[cfg(feature = "discovery")]
1667pub(crate) fn peek_plain_discovery_payload(
1668    buf: &[u8],
1669    expected: DataType,
1670) -> TelemetryResult<Option<(u32, &[u8])>> {
1671    // Avoid a second CRC pass for ordinary telemetry. Its existing receive
1672    // and decode path remains responsible for complete frame validation.
1673    let mut prelude = ByteReader::new(buf);
1674    prelude.read_bytes(2)?;
1675    if data_type_id_from_wire(read_uleb128(&mut prelude)?)? != expected.as_u32() {
1676        return Ok(None);
1677    }
1678    let data = verify_crc32(buf)?;
1679    let mut reader = ByteReader::new(data);
1680    let envelope = read_routing_envelope(&mut reader)?;
1681    if envelope.ty != expected
1682        || data[0] & (FLAG_COMPRESSED_PAYLOAD | 0x10) != 0
1683        || envelope.target_senders().len() != 0
1684        || envelope
1685            .wire_shape
1686            .is_some_and(|shape| shape != crate::message_meta(expected).element)
1687    {
1688        return Ok(None);
1689    }
1690    if envelope.is_reliable || envelope.has_reliable_header {
1691        let reliable =
1692            read_reliable_header_encoded(&mut reader, data[0] & FLAG_COMPACT_RELIABLE_HEADER != 0)?;
1693        // Only the normal unsequenced broadcast snapshot is a no-op. A
1694        // sequenced snapshot or piggyback ACK still needs reliable processing.
1695        if reliable.flags != RELIABLE_FLAG_UNSEQUENCED || reliable.seq != 0 || reliable.ack != 0 {
1696            return Ok(None);
1697        }
1698    }
1699    // The routing parser already validated endpoints and any inline contract.
1700    // Recover the logical size from the short prelude without owning arrays.
1701    let mut prelude = ByteReader::new(data);
1702    prelude.read_bytes(2)?;
1703    read_uleb128(&mut prelude)?;
1704    let size = usize::try_from(read_uleb128(&mut prelude)?)
1705        .map_err(|_| TelemetryError::Unpack("discovery payload too large"))?;
1706    if reader.remaining() != size {
1707        return Err(TelemetryError::Unpack("discovery payload length mismatch"));
1708    }
1709    Ok(Some((envelope.source_address, reader.read_bytes(size)?)))
1710}
1711
1712pub(crate) fn peek_routing_frame_info(buf: &[u8]) -> TelemetryResult<RoutingFrameInfo<'_>> {
1713    let data = verify_crc32(buf)?;
1714    let mut reader = ByteReader::new(data);
1715    let envelope = read_routing_envelope(&mut reader)?;
1716    let reliable = if envelope.is_reliable || envelope.has_reliable_header {
1717        Some(read_reliable_header_encoded(
1718            &mut reader,
1719            data[0] & FLAG_COMPACT_RELIABLE_HEADER != 0,
1720        )?)
1721    } else {
1722        None
1723    };
1724    let empty_payload = envelope.payload_is_empty && reader.remaining() == 0;
1725    Ok(RoutingFrameInfo {
1726        envelope,
1727        reliable,
1728        empty_payload,
1729    })
1730}
1731
1732fn peek_frame_info_inner(buf: &[u8]) -> TelemetryResult<TelemetryFrameInfo> {
1733    let mut r = ByteReader::new(buf);
1734    let header = read_routing_envelope(&mut r)?;
1735    let reliable = if header.is_reliable || header.has_reliable_header {
1736        Some(read_reliable_header_encoded(
1737            &mut r,
1738            buf[0] & FLAG_COMPACT_RELIABLE_HEADER != 0,
1739        )?)
1740    } else {
1741        None
1742    };
1743    Ok(TelemetryFrameInfo {
1744        envelope: header.into_owned(),
1745        reliable,
1746    })
1747}
1748
1749/// Peek the envelope plus reliable header (if present) without decoding payload bytes.
1750///
1751/// This is the primary router/relay fast path for reliable-layer decisions on
1752/// packed traffic. It still validates the frame CRC before exposing any
1753/// information.
1754pub fn peek_frame_info(buf: &[u8]) -> TelemetryResult<TelemetryFrameInfo> {
1755    let data = verify_crc32(buf)?;
1756    peek_frame_info_inner(data)
1757}
1758
1759/// Peek the envelope plus reliable header (if present) without validating CRC32.
1760///
1761/// This is intended only for internal call sites that have already validated
1762/// frame integrity or intentionally want best-effort inspection of partially
1763/// trusted bytes.
1764pub fn peek_frame_info_unchecked(buf: &[u8]) -> TelemetryResult<TelemetryFrameInfo> {
1765    let (data, _crc) = split_crc32(buf)?;
1766    peek_frame_info_inner(data)
1767}
1768
1769/// Locate the reliable-header byte offset within a packed frame.
1770///
1771/// The offset is computed after walking the source address and optional wire
1772/// contract. This matters because the contract can explicitly state that a
1773/// reliable header is present even if the current runtime schema no longer
1774/// marks the type as reliable.
1775///
1776/// # Parameters
1777/// - `buf`: Packed frame bytes, including the CRC32 trailer.
1778///
1779/// # Returns
1780/// - `Ok(Some(offset))` when a reliable header is present.
1781/// - `Ok(None)` when the frame carries no reliable header.
1782/// - `Err(TelemetryError)` when the frame is malformed.
1783pub fn reliable_header_offset(buf: &[u8]) -> TelemetryResult<Option<usize>> {
1784    Ok(reliable_header_span(buf)?.map(|(off, _, _)| off))
1785}
1786
1787pub(crate) fn reliable_header_span(
1788    buf: &[u8],
1789) -> TelemetryResult<Option<(usize, usize, ReliableHeader)>> {
1790    if buf.len() < CRC32_BYTES + 1 {
1791        return Err(TelemetryError::Unpack("short prelude"));
1792    }
1793    let data_len = buf.len().saturating_sub(CRC32_BYTES);
1794    let mut r = ByteReader::new(&buf[..data_len]);
1795
1796    let flags = r.read_bytes(1)?[0];
1797    let endpoint_bitmap_present = (flags & FLAG_ENDPOINT_BITMAP_PRESENT) != 0;
1798    let compact_reliable_header = (flags & FLAG_COMPACT_RELIABLE_HEADER) != 0;
1799
1800    let nep = r.read_bytes(1)?[0] as usize;
1801
1802    let ty_v = read_uleb128(&mut r)?;
1803    let _dsz = read_uleb128(&mut r)? as usize;
1804    let _ts_v = read_uleb128(&mut r)?;
1805    if (flags & FLAG_PACKET_NONCE) != 0 {
1806        let _ = read_uleb128(&mut r)?;
1807    }
1808    let _source_address = u32::try_from(read_uleb128(&mut r)?)
1809        .map_err(|_| TelemetryError::Unpack("source address too large"))?;
1810    let endpoint_bytes = if endpoint_bitmap_present {
1811        EP_BITMAP_BYTES
1812    } else {
1813        0
1814    };
1815
1816    if r.remaining() < endpoint_bytes {
1817        return Err(TelemetryError::Unpack("short buffer"));
1818    }
1819
1820    if flags & FLAG_ENDPOINT_LIST_PRESENT != 0 {
1821        if endpoint_bitmap_present {
1822            return Err(TelemetryError::Unpack("ambiguous endpoint encoding"));
1823        }
1824        read_endpoint_list(&mut r, nep)?;
1825    } else if endpoint_bitmap_present {
1826        r.read_bytes(EP_BITMAP_BYTES)?;
1827    }
1828    let contract = decode_wire_contract(&mut r, (flags & FLAG_WIRE_CONTRACT) != 0)?;
1829    let ty_u32 = data_type_id_from_wire(ty_v)?;
1830    let _ty = DataType(ty_u32);
1831    if flags & FLAG_RELIABLE_HEADER_PRESENT == 0 && !contract.has_reliable_header {
1832        return Ok(None);
1833    }
1834
1835    let off = r.off;
1836    let hdr = read_reliable_header_encoded(&mut r, compact_reliable_header)?;
1837    Ok(Some((off, r.off - off, hdr)))
1838}
1839
1840/// Rewrite the reliable header in-place and refresh the frame CRC32.
1841///
1842/// This avoids reserializing the entire packet when reliable transport code
1843/// only needs to change sequence/ACK state in an already-built frame.
1844///
1845/// # Parameters
1846/// - `buf`: Mutable packed frame bytes.
1847/// - `flags`: Replacement reliable-header flags byte.
1848/// - `seq`: Replacement sequence number.
1849/// - `ack`: Replacement cumulative ACK value.
1850///
1851/// # Returns
1852/// - `Ok(true)` if the frame carried a reliable header and it was rewritten.
1853/// - `Ok(false)` if no reliable header is present.
1854/// - `Err(TelemetryError)` if the frame is malformed.
1855pub fn rewrite_reliable_header(
1856    buf: &mut [u8],
1857    flags: u8,
1858    seq: u32,
1859    ack: u32,
1860) -> TelemetryResult<bool> {
1861    let Some((off, old_len, _)) = reliable_header_span(buf)? else {
1862        return Ok(false);
1863    };
1864    let hdr = ReliableHeader { flags, seq, ack };
1865    let compact = should_compact_reliable_header(hdr);
1866    if reliable_wire_size(hdr, compact) != old_len {
1867        return Err(TelemetryError::Unpack(
1868            "reliable header rewrite changes wire size",
1869        ));
1870    }
1871    let data_len = buf.len().saturating_sub(CRC32_BYTES);
1872    if data_len.saturating_sub(off) < old_len {
1873        return Err(TelemetryError::Unpack("short buffer"));
1874    }
1875    if compact {
1876        buf[0] |= FLAG_COMPACT_RELIABLE_HEADER;
1877    } else {
1878        buf[0] &= !FLAG_COMPACT_RELIABLE_HEADER;
1879    }
1880    let mut encoded = Vec::with_capacity(old_len);
1881    write_reliable_header_encoded(hdr, compact, &mut encoded);
1882    buf[off..off + old_len].copy_from_slice(&encoded);
1883    if buf.len() < CRC32_BYTES {
1884        return Err(TelemetryError::Unpack("short buffer"));
1885    }
1886    let crc = crc32_bytes(&buf[..data_len]);
1887    buf[data_len..data_len + CRC32_BYTES].copy_from_slice(&crc.to_le_bytes());
1888    Ok(true)
1889}
1890
1891pub(crate) fn rewrite_reliable_header_owned(
1892    buf: &[u8],
1893    flags: u8,
1894    seq: u32,
1895    ack: u32,
1896) -> TelemetryResult<Option<Arc<[u8]>>> {
1897    let Some((off, old_len, _)) = reliable_header_span(buf)? else {
1898        return Ok(None);
1899    };
1900    let data_len = buf.len().saturating_sub(CRC32_BYTES);
1901    if data_len < off + old_len {
1902        return Err(TelemetryError::Unpack("short buffer"));
1903    }
1904    let hdr = ReliableHeader { flags, seq, ack };
1905    let compact = should_compact_reliable_header(hdr);
1906    let mut encoded = Vec::with_capacity(reliable_wire_size(hdr, compact));
1907    write_reliable_header_encoded(hdr, compact, &mut encoded);
1908
1909    let mut out = Vec::with_capacity(data_len - old_len + encoded.len() + CRC32_BYTES);
1910    out.extend_from_slice(&buf[..off]);
1911    if compact {
1912        out[0] |= FLAG_COMPACT_RELIABLE_HEADER;
1913    } else {
1914        out[0] &= !FLAG_COMPACT_RELIABLE_HEADER;
1915    }
1916    out.extend_from_slice(&encoded);
1917    out.extend_from_slice(&buf[off + old_len..data_len]);
1918    let crc = crc32_bytes(&out);
1919    out.extend_from_slice(&crc.to_le_bytes());
1920    Ok(Some(Arc::from(out)))
1921}
1922
1923// ===========================================================================
1924// Size helpers
1925// ===========================================================================
1926
1927/// Compute the encoded metadata-prefix size for `pkt`.
1928///
1929/// This includes the fixed prelude and top-level varints, but excludes the
1930/// optional endpoint bitmap, optional wire contract, optional reliable header,
1931/// payload bytes, and CRC32 trailer.
1932///
1933/// # Parameters
1934/// - `pkt`: Packet to size.
1935///
1936/// # Returns
1937/// - Byte count of the packed metadata prefix.
1938pub fn header_size_bytes(pkt: &Packet) -> usize {
1939    let prelude = 2; // FLAGS (u8) + NEP (u8)
1940
1941    let source_address = sender_address_u32(pkt.sender());
1942
1943    prelude
1944        + uleb128_size(pkt.data_type().as_u32() as u64)
1945        + uleb128_size(pkt.data_size() as u64)
1946        + uleb128_size(pkt.timestamp())
1947        + if pkt.nonce() != 0 {
1948            uleb128_size(pkt.nonce() as u64)
1949        } else {
1950            0
1951        }
1952        + uleb128_size(source_address as u64)
1953}
1954
1955/// Compute the full packed wire size for `pkt`.
1956///
1957/// This applies the same sender/payload compression heuristics as normal
1958/// packing, then sums the metadata prefix, optional endpoint bitmap and frozen
1959/// wire contract, payload bytes, and CRC32 trailer. It matches [`pack_packet`];
1960/// its default unsequenced reliable header is included.
1961///
1962/// # Parameters
1963/// - `pkt`: Packet to size.
1964///
1965/// # Returns
1966/// - Total encoded frame size in bytes.
1967pub fn packet_wire_size(pkt: &Packet) -> usize {
1968    let shape = pkt.wire_shape();
1969    let targets = pkt.wire_target_senders();
1970    let contract_bytes = wire_contract_size(shape, targets);
1971    let contract_len = if shape.is_some() || !targets.is_empty() {
1972        uleb128_size(contract_bytes as u64) + contract_bytes
1973    } else {
1974        0
1975    };
1976    let endpoint_len = if packet_endpoint_bitmap_present(pkt, shape, targets) {
1977        if explicit_endpoint_list(pkt.endpoints()) {
1978            endpoint_list_size(pkt.endpoints())
1979        } else {
1980            EP_BITMAP_BYTES
1981        }
1982    } else {
1983        0
1984    };
1985    let (_, payload_wire) = payload_compression::compress_if_beneficial(pkt.payload());
1986    let reliable_len = if is_reliable_type(pkt.data_type()) {
1987        let header = ReliableHeader {
1988            flags: RELIABLE_FLAG_UNSEQUENCED,
1989            seq: 0,
1990            ack: 0,
1991        };
1992        reliable_wire_size(header, should_compact_reliable_header(header))
1993    } else {
1994        0
1995    };
1996    // Match pack_packet, including its default unsequenced reliable header.
1997    // No frame/header allocation, payload copy or CRC scan is needed for sizing.
1998    header_size_bytes(pkt)
1999        + endpoint_len
2000        + contract_len
2001        + reliable_len
2002        + payload_wire.len()
2003        + CRC32_BYTES
2004}
2005
2006#[inline]
2007/// Computes the same packet ID as [`Packet::packet_id`] directly from a packed wire frame.
2008pub fn packet_id_from_wire(buf: &[u8]) -> Result<u64, TelemetryError> {
2009    let data = verify_crc32(buf)?;
2010    if data.len() < 2 {
2011        return Err(TelemetryError::Unpack("short prelude"));
2012    }
2013
2014    let mut r = ByteReader::new(data);
2015
2016    let flags = r.read_bytes(1)?[0];
2017    let payload_is_compressed = (flags & FLAG_COMPRESSED_PAYLOAD) != 0;
2018    let compact_reliable_header = (flags & FLAG_COMPACT_RELIABLE_HEADER) != 0;
2019    #[cfg(feature = "cryptography")]
2020    let payload_is_encrypted = (flags & FLAG_E2E_ENCRYPTED_PAYLOAD) != 0;
2021    #[cfg(not(feature = "cryptography"))]
2022    if (flags & 0x10) != 0 {
2023        return Err(TelemetryError::Unpack("e2e crypto unsupported"));
2024    }
2025
2026    let _nep = r.read_bytes(1)?[0] as usize;
2027
2028    let _ = read_uleb128(&mut r)?;
2029    let dsz = read_uleb128(&mut r)? as usize; // logical payload size (uncompressed)
2030    let ts_v = read_uleb128(&mut r)?;
2031    let nonce = if (flags & FLAG_PACKET_NONCE) != 0 {
2032        u16::try_from(read_uleb128(&mut r)?)
2033            .map_err(|_| TelemetryError::Unpack("packet nonce too large"))?
2034    } else {
2035        0
2036    };
2037    let source_address = u32::try_from(read_uleb128(&mut r)?)
2038        .map_err(|_| TelemetryError::Unpack("source address too large"))?;
2039    // Reuse the validated borrowed envelope. Computing an ID must not build
2040    // owned endpoint/target arrays merely to hash or inspect their contents.
2041    let mut envelope_reader = ByteReader::new(data);
2042    let envelope = read_routing_envelope(&mut envelope_reader)?;
2043    r = envelope_reader;
2044    let ty = envelope.ty;
2045
2046    // ---- reliable header (optional) ----
2047    if envelope.is_reliable || envelope.has_reliable_header {
2048        let hdr = read_reliable_header_encoded(&mut r, compact_reliable_header)?;
2049        if (hdr.flags & RELIABLE_FLAG_ACK_ONLY) != 0 {
2050            return Err(TelemetryError::Unpack("reliable control frame"));
2051        }
2052    }
2053
2054    // ---- payload bytes (must hash *logical* payload bytes) ----
2055    #[cfg(feature = "cryptography")]
2056    let payload_wire_owned;
2057    #[cfg(feature = "cryptography")]
2058    let payload_wire: &[u8] = if payload_is_encrypted {
2059        let aad_end = r.off;
2060        payload_wire_owned =
2061            read_encrypted_payload(&mut r, data.get(..aad_end).unwrap_or(&[]), dsz)?;
2062        &payload_wire_owned
2063    } else if !payload_is_compressed {
2064        if r.remaining() < dsz {
2065            return Err(TelemetryError::Unpack("short buffer"));
2066        }
2067        r.read_bytes(dsz)?
2068    } else {
2069        let comp_len = r.remaining();
2070        if comp_len < 1 {
2071            return Err(TelemetryError::Unpack("short buffer"));
2072        }
2073        r.read_bytes(comp_len)?
2074    };
2075    #[cfg(not(feature = "cryptography"))]
2076    let payload_wire: &[u8] = if !payload_is_compressed {
2077        if r.remaining() < dsz {
2078            return Err(TelemetryError::Unpack("short buffer"));
2079        }
2080        r.read_bytes(dsz)?
2081    } else {
2082        let comp_len = r.remaining();
2083        if comp_len < 1 {
2084            return Err(TelemetryError::Unpack("short buffer"));
2085        }
2086        r.read_bytes(comp_len)?
2087    };
2088    let payload_decompressed;
2089    let payload_bytes: &[u8] = if payload_is_compressed {
2090        payload_decompressed = payload_compression::decompress(payload_wire, dsz)?;
2091        &payload_decompressed
2092    } else {
2093        if payload_wire.len() != dsz {
2094            return Err(TelemetryError::Unpack("payload length mismatch"));
2095        }
2096        payload_wire
2097    };
2098
2099    // ---- hash exactly like Packet::packet_id() ----
2100    let mut h: u64 = 0x9E37_79B9_7F4A_7C15;
2101
2102    // Compact source address.
2103    h = hash_bytes_u64(h, &source_address.to_le_bytes());
2104
2105    // Stable numeric identity is independent of when schema names are learned.
2106    h = hash_bytes_u64(h, &ty.0.to_le_bytes());
2107
2108    // Endpoints as string bytes, in ascending discriminant order.
2109    for ep in envelope.endpoints() {
2110        h = hash_bytes_u64(h, &ep.0.to_le_bytes());
2111    }
2112
2113    // Timestamp + data_size as bytes
2114    h = hash_bytes_u64(h, &ts_v.to_le_bytes());
2115    h = hash_bytes_u64(h, &nonce.to_le_bytes());
2116    h = hash_bytes_u64(h, &(dsz as u64).to_le_bytes());
2117
2118    // Payload bytes (logical payload)
2119    h = hash_bytes_u64(h, payload_bytes);
2120    Ok(h)
2121}
2122
2123mod payload_compression {
2124    use crate::TelemetryError;
2125    use alloc::borrow::Cow;
2126    #[cfg(feature = "compression")]
2127    use alloc::vec;
2128    use alloc::vec::Vec;
2129
2130    #[cfg(feature = "compression")]
2131    use crate::config::runtime_payload_compress_threshold;
2132    #[cfg(feature = "compression")]
2133    use zstd_safe::CompressionLevel;
2134
2135    /// Compress the given payload if it is beneficial to do so.
2136    /// # Arguments
2137    /// - `payload`: Original uncompressed payload bytes.
2138    /// # Returns
2139    /// - `(bool, Cow<[u8]>)`: Tuple where the first element indicates whether
2140    ///   compression was applied, and the second element is the resulting
2141    ///   payload bytes (compressed or original).
2142    #[cfg(feature = "compression")]
2143    pub fn compress_if_beneficial(payload: &'_ [u8]) -> (bool, Cow<'_, [u8]>) {
2144        if payload.len() < runtime_payload_compress_threshold() {
2145            return (false, Cow::Borrowed(payload));
2146        }
2147
2148        // Bound output and avoid growth beyond useful threshold.
2149        let Some(compressed) = compress_to_vec_bounded(payload, payload.len().saturating_sub(2))
2150        else {
2151            return (false, Cow::Borrowed(payload));
2152        };
2153
2154        // Only use compressed form if it actually saves space.
2155        if compressed.len() + 1 >= payload.len() {
2156            (false, Cow::Borrowed(payload))
2157        } else {
2158            (true, Cow::Owned(compressed))
2159        }
2160    }
2161
2162    #[cfg(feature = "compression")]
2163    fn compress_to_vec_bounded(input: &[u8], max_output: usize) -> Option<Vec<u8>> {
2164        if input.is_empty() || max_output == 0 {
2165            return None;
2166        }
2167
2168        let mut out = vec![0u8; max_output];
2169        // Use default-level behavior for better compression ratio on typical telemetry payloads.
2170        let level: CompressionLevel = 1;
2171        let written = zstd_safe::compress(&mut out[..], input, level).ok()?;
2172        out.truncate(written);
2173        Some(out)
2174    }
2175
2176    /// Decompress the given compressed payload.
2177    /// # Arguments
2178    /// - `compressed`: Compressed payload bytes.
2179    /// - `expected_len`: Expected length of the decompressed payload.
2180    /// # Returns
2181    /// - `Vec<u8>`: Decompressed payload bytes.
2182    /// # Errors
2183    /// - `TelemetryError::Unpack` if decompression fails or the size
2184    ///   does not match `expected_len`.
2185    #[cfg(feature = "compression")]
2186    pub fn decompress(compressed: &[u8], expected_len: usize) -> Result<Vec<u8>, TelemetryError> {
2187        let mut out = vec![0u8; expected_len];
2188        let written = zstd_safe::decompress(&mut out[..], compressed)
2189            .map_err(|_| TelemetryError::Unpack("decompression failed"))?;
2190        if written != expected_len {
2191            return Err(TelemetryError::Unpack("decompressed size mismatch"));
2192        }
2193        Ok(out)
2194    }
2195
2196    // Stub when compression is disabled (never actually produces compressed payloads).
2197    #[cfg(not(feature = "compression"))]
2198    /// Returns the original payload unchanged when compression support is disabled.
2199    pub fn compress_if_beneficial<'a>(payload: &'a [u8]) -> (bool, Cow<'a, [u8]>) {
2200        (false, Cow::Borrowed(payload))
2201    }
2202
2203    #[cfg(not(feature = "compression"))]
2204    /// Reports that compressed payloads cannot be decoded when compression support is disabled.
2205    pub fn decompress(_compressed: &[u8], _expected_len: usize) -> Result<Vec<u8>, TelemetryError> {
2206        Err(TelemetryError::Unpack(
2207            "compressed payloads not supported (compression feature disabled)",
2208        ))
2209    }
2210}
2211
2212#[cfg(test)]
2213mod endpoint_decode_tests {
2214    use super::*;
2215
2216    #[test]
2217    fn numeric_sender_alias_keeps_packet_wire_and_receipt_identity() {
2218        let ty = DataType::ReliableAck;
2219        let make = |sender: &str| {
2220            Packet::new_with_wire_contract(
2221                ty,
2222                message_meta(ty).endpoints_ref(),
2223                sender,
2224                200,
2225                9,
2226                Arc::from(42u64.to_le_bytes()),
2227                Some(MessageElement::Static(
2228                    2,
2229                    crate::MessageDataType::UInt32,
2230                    crate::MessageClass::Data,
2231                )),
2232                Arc::from([123u64]),
2233            )
2234            .unwrap()
2235        };
2236        let named = make("FC");
2237        let alias = alloc::format!("@addr:{}", sender_address_u32("FC"));
2238        let unresolved = make(&alias);
2239        assert_eq!(unresolved.packet_id(), named.packet_id());
2240        let named_wire = pack_packet(&named);
2241        let alias_wire = pack_packet(&unresolved);
2242        assert_eq!(alias_wire, named_wire);
2243        assert_eq!(packet_id_from_wire(&alias_wire).unwrap(), named.packet_id());
2244        assert_eq!(
2245            unpack_packet(&alias_wire).unwrap().packet_id(),
2246            named.packet_id()
2247        );
2248    }
2249
2250    #[test]
2251    fn scheduling_type_is_bounded_and_never_replaces_frame_validation() {
2252        let pkt = Packet::new_with_wire_contract(
2253            DataType::ReliableAck,
2254            message_meta(DataType::ReliableAck).endpoints_ref(),
2255            "E2EACK:GS",
2256            100,
2257            1,
2258            Arc::from(42u64.to_le_bytes()),
2259            Some(MessageElement::Static(
2260                2,
2261                crate::MessageDataType::UInt32,
2262                crate::MessageClass::Data,
2263            )),
2264            Arc::from([123u64]),
2265        )
2266        .unwrap();
2267        let mut bytes = pack_packet(&pkt).to_vec();
2268        assert_eq!(scheduling_type(&bytes), Some(DataType::ReliableAck));
2269        let last = bytes.len() - 1;
2270        bytes[last] ^= 1;
2271        assert_eq!(scheduling_type(&bytes), Some(DataType::ReliableAck));
2272        assert!(peek_routing_envelope(&bytes).is_err());
2273        assert!(unpack_packet(&bytes).is_err());
2274        assert_eq!(scheduling_type(&[0, 0, 0x80]), None);
2275        assert_eq!(scheduling_type(&[0xff; 1024]), None);
2276        assert_eq!(scheduling_type(&[]), None);
2277    }
2278
2279    #[test]
2280    fn receipt_contract_omits_redundant_builtin_endpoint_bitmap() {
2281        for ty in [
2282            DataType::ReliableAck,
2283            DataType::ReliablePartialAck,
2284            DataType::ReliablePacketRequest,
2285        ] {
2286            let shape = Some(MessageElement::Static(
2287                2,
2288                crate::MessageDataType::UInt32,
2289                crate::MessageClass::Data,
2290            ));
2291            let targets: Arc<[u64]> = Arc::from([0xa7fcbb129e92435f, 0x41426f62de53eb28]);
2292            let payload: Arc<[u8]> = Arc::from(0x9c2a63d6d7d44100u64.to_le_bytes());
2293            let make = |eps: &[DataEndpoint]| {
2294                Packet::new_with_wire_contract(
2295                    ty,
2296                    eps,
2297                    "E2EACK:GS",
2298                    1791737696510,
2299                    1234,
2300                    payload.clone(),
2301                    shape,
2302                    targets.clone(),
2303                )
2304                .unwrap()
2305            };
2306            let packet = make(message_meta(ty).endpoints_ref());
2307            let packed = pack_packet(&packet);
2308            assert_eq!(packed[0] & FLAG_ENDPOINT_BITMAP_PRESENT, 0);
2309            assert_ne!(packed[0] & FLAG_WIRE_CONTRACT, 0);
2310            let routing = peek_routing_envelope(&packed).unwrap();
2311            assert_eq!(routing.endpoints().collect::<Vec<_>>(), packet.endpoints());
2312            assert_eq!(
2313                routing.target_senders().collect::<Vec<_>>(),
2314                targets.as_ref()
2315            );
2316            let decoded = unpack_packet(&packed).unwrap();
2317            assert_eq!(decoded.packet_id(), packet.packet_id());
2318            assert_eq!(decoded.payload(), packet.payload());
2319            assert_eq!(decoded.wire_target_senders(), targets.as_ref());
2320            // Sparse explicit endpoints use a short sorted list.
2321            let explicit = pack_packet(&make(&[DataEndpoint::Discovery]));
2322            assert_ne!(explicit[0] & FLAG_ENDPOINT_LIST_PRESENT, 0);
2323            assert_eq!(
2324                explicit.len(),
2325                packed.len() + uleb128_size(u64::from(DataEndpoint::Discovery.0))
2326            );
2327            assert_eq!(
2328                unpack_packet(&explicit).unwrap().endpoints(),
2329                &[DataEndpoint::Discovery]
2330            );
2331        }
2332    }
2333
2334    #[test]
2335    fn borrowed_routing_header_preserves_targets_and_validates_truncated_contracts() {
2336        let targets: Arc<[u64]> = Arc::from([1, u64::MAX, 42]);
2337        let packet = Packet::new_with_wire_contract(
2338            DataType::DiscoverySchema,
2339            &[DataEndpoint::Discovery],
2340            "PEER",
2341            1234,
2342            7,
2343            Arc::from([1u8, 2, 3]),
2344            Some(MessageElement::Dynamic(
2345                crate::MessageDataType::Binary,
2346                crate::MessageClass::Data,
2347            )),
2348            targets.clone(),
2349        )
2350        .unwrap();
2351        let packed = pack_packet(&packet);
2352        let routing = peek_routing_envelope(&packed).unwrap();
2353        let full = unpack_packet(&packed).unwrap();
2354        assert_eq!(routing.ty, full.data_type());
2355        assert_eq!(routing.endpoints().collect::<Vec<_>>(), full.endpoints());
2356        assert_eq!(
2357            routing.target_senders().collect::<Vec<_>>(),
2358            targets.as_ref()
2359        );
2360        assert_eq!(routing.timestamp_ms, full.timestamp());
2361        // Cut through the header, recomputing CRC so truncation validation runs.
2362        let data = verify_crc32(&packed).unwrap();
2363        let mut reader = ByteReader::new(data);
2364        read_routing_envelope(&mut reader).unwrap();
2365        for end in 0..reader.off {
2366            let mut truncated = data[..end].to_vec();
2367            append_crc32(&mut truncated);
2368            assert!(
2369                peek_routing_envelope(&truncated).is_err(),
2370                "accepted cut at {end}"
2371            );
2372        }
2373    }
2374
2375    #[test]
2376    fn large_packet_with_long_prefix_does_not_grow_packing_buffer() {
2377        let payload: Vec<u8> = (0..3500)
2378            .map(|i| ((i * 73 + i / 251) % 256) as u8)
2379            .collect();
2380        let packet = Packet::new(
2381            DataType::DiscoverySchema,
2382            &[DataEndpoint::Discovery],
2383            "LONG_PREFIX_SENDER",
2384            u64::MAX,
2385            payload.into(),
2386        )
2387        .unwrap()
2388        .with_nonce(u16::MAX);
2389        assert!(header_size_bytes(&packet) > 16);
2390        let packed = pack_packet(&packet);
2391        let unpacked = unpack_packet(&packed).unwrap();
2392        assert_eq!(unpacked.payload(), packet.payload());
2393        assert_eq!(unpacked.timestamp(), u64::MAX);
2394        assert_eq!(unpacked.nonce(), u16::MAX);
2395    }
2396
2397    #[test]
2398    fn bitmap_decode_preserves_sorted_unique_destinations() {
2399        let input = [
2400            DataEndpoint::TelemetryError,
2401            DataEndpoint::TimeSync,
2402            DataEndpoint::Discovery,
2403            DataEndpoint::TimeSync,
2404        ];
2405        let decoded = expand_endpoint_bitmap(&build_endpoint_bitmap(&input)).unwrap();
2406        assert_eq!(
2407            &*decoded,
2408            &[
2409                DataEndpoint::TimeSync,
2410                DataEndpoint::Discovery,
2411                DataEndpoint::TelemetryError
2412            ]
2413        );
2414        assert!(
2415            expand_endpoint_bitmap(&[0; EP_BITMAP_BYTES])
2416                .unwrap()
2417                .is_empty()
2418        );
2419        assert!(expand_endpoint_bitmap(&[0; EP_BITMAP_BYTES - 1]).is_err());
2420    }
2421
2422    #[test]
2423    fn bitmap_decode_rejects_unregistered_endpoint_bits() {
2424        if let Some(id) =
2425            (0..=MAX_VALUE_DATA_ENDPOINT).find(|id| DataEndpoint::try_from_u32(*id).is_none())
2426        {
2427            let mut bitmap = [0; EP_BITMAP_BYTES];
2428            bitmap[id as usize / 8] = 1 << (id % 8);
2429            assert!(expand_endpoint_bitmap(&bitmap).is_err());
2430        }
2431    }
2432}