ndn_engine/pipeline/
context.rs

1use std::sync::Arc;
2
3use bytes::Bytes;
4use smallvec::SmallVec;
5
6use ndn_packet::{Data, Interest, Nack, Name};
7use ndn_store::{NameHashes, PitToken};
8use ndn_transport::{AnyMap, FaceId};
9
10/// The packet as it progresses through decode stages.
11pub enum DecodedPacket {
12    /// Not yet decoded — the raw bytes are still in `PacketContext::raw_bytes`.
13    Raw,
14    Interest(Box<Interest>),
15    Data(Box<Data>),
16    Nack(Box<Nack>),
17}
18
19/// Per-packet state passed by value through pipeline stages.
20///
21/// Passing by value (rather than `&mut`) makes ownership explicit:
22/// a stage that short-circuits simply does not return the context,
23/// so Rust's ownership system prevents use-after-hand-off at compile time.
24pub struct PacketContext {
25    /// Wire-format bytes of the original packet.
26    pub raw_bytes: Bytes,
27    /// Face the packet arrived on.
28    pub face_id: FaceId,
29    /// Decoded name — hoisted to top level because every stage needs it.
30    /// `None` until `TlvDecodeStage` runs.
31    pub name: Option<Arc<Name>>,
32    /// Decoded packet — starts as `Raw`, transitions after TlvDecodeStage.
33    pub packet: DecodedPacket,
34    /// Pre-computed cumulative name-prefix hashes.  Set by `TlvDecodeStage`
35    /// immediately after the name is known; used by PIT and FIB stages to
36    /// avoid re-hashing name components on every probe.
37    pub name_hashes: Option<NameHashes>,
38    /// PIT token — written by PitCheckStage, `None` before that stage runs.
39    pub pit_token: Option<PitToken>,
40    /// NDNLPv2 PIT token (opaque, 1-32 bytes) from the incoming LP header.
41    /// Distinct from the internal `pit_token` hash — this is the wire-protocol
42    /// hop-by-hop token that must be echoed in Data/Nack responses.
43    pub lp_pit_token: Option<Bytes>,
44    /// Faces selected for forwarding by the strategy stage.
45    pub out_faces: SmallVec<[FaceId; 4]>,
46    /// Set to `true` by CsLookupStage on a cache hit.
47    pub cs_hit: bool,
48    /// Set to `true` by the security validation stage.
49    pub verified: bool,
50    /// Arrival time in nanoseconds since the Unix epoch (set by the face task).
51    pub arrival: u64,
52    /// Escape hatch for inter-stage communication not covered by explicit fields.
53    /// Use sparingly; prefer explicit fields for anything the core pipeline touches.
54    pub tags: AnyMap,
55}
56
57impl PacketContext {
58    pub fn new(raw_bytes: Bytes, face_id: FaceId, arrival: u64) -> Self {
59        Self {
60            raw_bytes,
61            face_id,
62            name: None,
63            packet: DecodedPacket::Raw,
64            name_hashes: None,
65            pit_token: None,
66            lp_pit_token: None,
67            out_faces: SmallVec::new(),
68            cs_hit: false,
69            verified: false,
70            arrival,
71            tags: AnyMap::new(),
72        }
73    }
74}
75
76#[cfg(test)]
77mod tests {
78    use super::*;
79    use bytes::Bytes;
80    use ndn_transport::FaceId;
81
82    #[test]
83    fn packet_context_new_defaults() {
84        let raw = Bytes::from_static(b"\x05\x01\x00");
85        let ctx = PacketContext::new(raw.clone(), FaceId(7), 12345);
86        assert_eq!(ctx.raw_bytes, raw);
87        assert_eq!(ctx.face_id, FaceId(7));
88        assert_eq!(ctx.arrival, 12345);
89        assert!(ctx.name.is_none());
90        assert!(ctx.pit_token.is_none());
91        assert!(ctx.out_faces.is_empty());
92        assert!(!ctx.cs_hit);
93        assert!(!ctx.verified);
94        assert!(matches!(ctx.packet, DecodedPacket::Raw));
95    }
96}