ndn_transport/
face_pair_table.rs

1#[cfg(not(target_arch = "wasm32"))]
2use dashmap::DashMap;
3
4use crate::FaceId;
5
6/// Maps rx `FaceId` → tx `FaceId` for asymmetric link pairs (e.g., wfb-ng).
7///
8/// On symmetric faces (Udp, Tcp, Ethernet), Data is sent back on the same face
9/// an Interest arrived on. On asymmetric wfb-ng links, there is a separate
10/// transmit face — this table resolves which tx face to use.
11///
12/// The dispatch stage consults this table before sending Data:
13///
14/// ```ignore
15/// let send_id = face_pairs.get_tx_for_rx(in_face_id).unwrap_or(in_face_id);
16/// face_table.get(send_id)?.send(data).await;
17/// ```
18///
19/// Normal faces have no entry in this table (`get_tx_for_rx` returns `None`),
20/// so `unwrap_or(in_face_id)` falls through to the standard symmetric path.
21pub struct FacePairTable {
22    #[cfg(not(target_arch = "wasm32"))]
23    pairs: DashMap<FaceId, FaceId>,
24    #[cfg(target_arch = "wasm32")]
25    pairs: std::sync::Mutex<std::collections::HashMap<FaceId, FaceId>>,
26}
27
28impl FacePairTable {
29    pub fn new() -> Self {
30        Self {
31            #[cfg(not(target_arch = "wasm32"))]
32            pairs: DashMap::new(),
33            #[cfg(target_arch = "wasm32")]
34            pairs: std::sync::Mutex::new(std::collections::HashMap::new()),
35        }
36    }
37
38    /// Register an asymmetric link pair: Interests arrive on `rx`, Data is
39    /// sent on `tx`.
40    pub fn insert(&self, rx: FaceId, tx: FaceId) {
41        #[cfg(not(target_arch = "wasm32"))]
42        self.pairs.insert(rx, tx);
43        #[cfg(target_arch = "wasm32")]
44        self.pairs.lock().unwrap().insert(rx, tx);
45    }
46
47    /// Returns the tx face to use when Data should go back on `rx_face`.
48    /// Returns `None` for symmetric faces.
49    pub fn get_tx_for_rx(&self, rx: FaceId) -> Option<FaceId> {
50        #[cfg(not(target_arch = "wasm32"))]
51        return self.pairs.get(&rx).map(|r| *r);
52        #[cfg(target_arch = "wasm32")]
53        return self.pairs.lock().unwrap().get(&rx).copied();
54    }
55
56    /// Remove the pair for `rx`.
57    pub fn remove(&self, rx: FaceId) {
58        #[cfg(not(target_arch = "wasm32"))]
59        self.pairs.remove(&rx);
60        #[cfg(target_arch = "wasm32")]
61        self.pairs.lock().unwrap().remove(&rx);
62    }
63
64    pub fn len(&self) -> usize {
65        #[cfg(not(target_arch = "wasm32"))]
66        return self.pairs.len();
67        #[cfg(target_arch = "wasm32")]
68        return self.pairs.lock().unwrap().len();
69    }
70
71    pub fn is_empty(&self) -> bool {
72        #[cfg(not(target_arch = "wasm32"))]
73        return self.pairs.is_empty();
74        #[cfg(target_arch = "wasm32")]
75        return self.pairs.lock().unwrap().is_empty();
76    }
77}
78
79impl Default for FacePairTable {
80    fn default() -> Self {
81        Self::new()
82    }
83}
84
85#[cfg(test)]
86mod tests {
87    use super::*;
88
89    fn id(n: u32) -> FaceId {
90        FaceId(n)
91    }
92
93    #[test]
94    fn get_tx_for_unknown_rx_returns_none() {
95        let t = FacePairTable::new();
96        assert!(t.get_tx_for_rx(id(1)).is_none());
97    }
98
99    #[test]
100    fn insert_then_get_returns_tx() {
101        let t = FacePairTable::new();
102        t.insert(id(1), id(2));
103        assert_eq!(t.get_tx_for_rx(id(1)), Some(id(2)));
104    }
105
106    #[test]
107    fn remove_clears_pair() {
108        let t = FacePairTable::new();
109        t.insert(id(3), id(4));
110        t.remove(id(3));
111        assert!(t.get_tx_for_rx(id(3)).is_none());
112    }
113
114    #[test]
115    fn symmetric_face_returns_none() {
116        let t = FacePairTable::new();
117        t.insert(id(10), id(11));
118        // Face 99 is symmetric — not in the table.
119        assert!(t.get_tx_for_rx(id(99)).is_none());
120    }
121
122    #[test]
123    fn multiple_pairs_independent() {
124        let t = FacePairTable::new();
125        t.insert(id(1), id(2));
126        t.insert(id(3), id(4));
127        assert_eq!(t.get_tx_for_rx(id(1)), Some(id(2)));
128        assert_eq!(t.get_tx_for_rx(id(3)), Some(id(4)));
129        assert_eq!(t.len(), 2);
130    }
131
132    #[test]
133    fn is_empty_and_len() {
134        let t = FacePairTable::new();
135        assert!(t.is_empty());
136        t.insert(id(0), id(1));
137        assert!(!t.is_empty());
138        assert_eq!(t.len(), 1);
139    }
140}