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}