Skip to main content

packet_parser/timing/
mod.rs

1#[cfg(feature = "parse_timing")]
2use std::default;
3
4// packet_parser/src/timing.rs
5#[cfg(feature = "parse_timing")]
6#[derive(Debug, Clone, Copy, Default)]
7pub struct ParseTiming {
8    pub l2_ns: u64,
9    pub l3_ns: u64,
10    pub l4_ns: u64,
11    pub l7_ns: u64,
12    pub total_ns: u64,
13}
14
15#[cfg(feature = "parse_timing")]
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
17pub enum LayerAttempt {
18    #[default]
19    Skipped = 0,
20    Ok = 1,
21    Unsupported = 2,
22}
23
24#[cfg(feature = "parse_timing")]
25#[derive(Debug, Clone, Copy, Default)]
26pub struct ParseReport {
27    pub timing: ParseTiming,
28    pub l3: LayerAttempt,
29    pub l4: LayerAttempt,
30    pub l7: LayerAttempt,
31}
32
33#[cfg(not(feature = "parse_timing"))]
34#[derive(Debug, Clone, Copy, Default)]
35pub struct ParseTiming;
36
37// packet_parser/src/timing.rs
38#[cfg(feature = "parse_timing")]
39#[inline(always)]
40pub fn now() -> std::time::Instant {
41    std::time::Instant::now()
42}
43
44#[cfg(feature = "parse_timing")]
45#[inline(always)]
46pub fn elapsed_ns(t0: std::time::Instant) -> u64 {
47    t0.elapsed().as_nanos() as u64
48}
49
50#[cfg(not(feature = "parse_timing"))]
51#[inline(always)]
52pub fn now() {}
53
54#[cfg(not(feature = "parse_timing"))]
55#[inline(always)]
56pub fn elapsed_ns(_: ()) -> u64 {
57    0
58}
59
60// packet_parser/src/timing.rs
61#[cfg(feature = "parse_timing")]
62#[macro_export]
63macro_rules! time_block_ns {
64    ($dst:expr, $body:block) => {{
65        let t0 = $crate::timing::now();
66        let out = $body;
67        *$dst = $crate::timing::elapsed_ns(t0);
68        out
69    }};
70}
71
72#[cfg(not(feature = "parse_timing"))]
73#[macro_export]
74macro_rules! time_block_ns {
75    ($dst:expr, $body:block) => {{
76        let _ = $dst;
77        $body
78    }};
79}
80
81#[cfg(test)]
82mod tests {
83    use super::*;
84
85    #[cfg(feature = "parse_timing")]
86    #[test]
87    fn test_parse_timing_default() {
88        let timing = ParseTiming::default();
89
90        assert_eq!(timing.l2_ns, 0);
91        assert_eq!(timing.l3_ns, 0);
92        assert_eq!(timing.l4_ns, 0);
93        assert_eq!(timing.l7_ns, 0);
94        assert_eq!(timing.total_ns, 0);
95    }
96
97    #[cfg(feature = "parse_timing")]
98    #[test]
99    fn test_layer_attempt_default_is_skipped() {
100        let attempt = LayerAttempt::default();
101        assert_eq!(attempt, LayerAttempt::Skipped);
102    }
103
104    #[cfg(feature = "parse_timing")]
105    #[test]
106    fn test_layer_attempt_variants_values() {
107        assert_eq!(LayerAttempt::Skipped as u8, 0);
108        assert_eq!(LayerAttempt::Ok as u8, 1);
109        assert_eq!(LayerAttempt::Unsupported as u8, 2);
110    }
111
112    #[cfg(feature = "parse_timing")]
113    #[test]
114    fn test_parse_report_default() {
115        let report = ParseReport::default();
116
117        assert_eq!(report.timing.l2_ns, 0);
118        assert_eq!(report.timing.l3_ns, 0);
119        assert_eq!(report.timing.l4_ns, 0);
120        assert_eq!(report.timing.l7_ns, 0);
121        assert_eq!(report.timing.total_ns, 0);
122
123        assert_eq!(report.l3, LayerAttempt::Skipped);
124        assert_eq!(report.l4, LayerAttempt::Skipped);
125        assert_eq!(report.l7, LayerAttempt::Skipped);
126    }
127
128    #[cfg(feature = "parse_timing")]
129    #[test]
130    fn test_now_and_elapsed_ns() {
131        let t0 = now();
132        let elapsed = elapsed_ns(t0);
133
134        assert!(elapsed <= u64::MAX);
135    }
136
137    #[cfg(feature = "parse_timing")]
138    #[test]
139    fn test_elapsed_ns_is_non_zero_after_work() {
140        let t0 = now();
141
142        let mut acc = 0u64;
143        for i in 0..10_000 {
144            acc = acc.wrapping_add(i);
145        }
146
147        let elapsed = elapsed_ns(t0);
148
149        assert!(acc > 0);
150        assert!(elapsed > 0);
151    }
152
153    #[cfg(feature = "parse_timing")]
154    #[test]
155    fn test_time_block_ns_sets_duration_and_returns_value() {
156        let mut measured = 0u64;
157
158        let result = time_block_ns!(&mut measured, {
159            let mut sum = 0u64;
160            for i in 0..1_000 {
161                sum += i;
162            }
163            sum
164        });
165
166        assert_eq!(result, (0..1_000u64).sum::<u64>());
167        assert!(measured > 0);
168    }
169
170    #[cfg(feature = "parse_timing")]
171    #[test]
172    fn test_time_block_ns_with_unit_return() {
173        let mut measured = 0u64;
174        let mut value = 0u32;
175
176        time_block_ns!(&mut measured, {
177            value = 42;
178        });
179
180        assert_eq!(value, 42);
181        assert!(measured > 0);
182    }
183
184    #[cfg(not(feature = "parse_timing"))]
185    #[test]
186    fn test_parse_timing_default_without_feature() {
187        let _timing = ParseTiming;
188    }
189
190    #[cfg(not(feature = "parse_timing"))]
191    #[test]
192    fn test_now_without_feature() {
193        now();
194        let elapsed = elapsed_ns(());
195
196        assert_eq!(elapsed, 0);
197    }
198
199    #[cfg(not(feature = "parse_timing"))]
200    #[test]
201    fn test_time_block_ns_without_feature_returns_value_and_does_not_modify_dst() {
202        let mut measured = 123u64;
203
204        let result = time_block_ns!(&mut measured, {
205            let mut sum = 0u64;
206            for i in 0..10 {
207                sum += i;
208            }
209            sum
210        });
211
212        assert_eq!(result, 45);
213        assert_eq!(measured, 123);
214    }
215
216    #[cfg(not(feature = "parse_timing"))]
217    #[test]
218    fn test_time_block_ns_without_feature_with_unit_return() {
219        let mut measured = 999u64;
220        let value: u32;
221
222        time_block_ns!(&mut measured, {
223            value = 7;
224        });
225
226        assert_eq!(value, 7);
227        assert_eq!(measured, 999);
228    }
229}