rns_net/common/
interface_stats.rs1pub const ANNOUNCE_SAMPLE_MAX: usize = 48;
3
4pub const PATH_REQUEST_SAMPLE_MAX: usize = 48;
6
7pub const INCOMING_ANNOUNCE_MIN_SAMPLE: usize = 2;
9
10pub const INCOMING_PATH_REQUEST_MIN_SAMPLE: usize = 2;
12
13#[derive(Debug, Clone, Default)]
15pub struct InterfaceStats {
16 pub rxb: u64,
17 pub txb: u64,
18 pub rx_packets: u64,
19 pub tx_packets: u64,
20 pub started: f64,
21 pub ia_timestamps: Vec<f64>,
23 pub oa_timestamps: Vec<f64>,
25 pub ip_timestamps: Vec<f64>,
27 pub op_timestamps: Vec<f64>,
29}
30
31impl InterfaceStats {
32 pub fn record_incoming_announce(&mut self, now: f64) {
34 self.ia_timestamps.push(now);
35 if self.ia_timestamps.len() > ANNOUNCE_SAMPLE_MAX {
36 self.ia_timestamps.remove(0);
37 }
38 }
39
40 pub fn record_outgoing_announce(&mut self, now: f64) {
42 self.oa_timestamps.push(now);
43 if self.oa_timestamps.len() > ANNOUNCE_SAMPLE_MAX {
44 self.oa_timestamps.remove(0);
45 }
46 }
47
48 pub fn record_incoming_path_request(&mut self, now: f64) {
50 self.ip_timestamps.push(now);
51 if self.ip_timestamps.len() > PATH_REQUEST_SAMPLE_MAX {
52 self.ip_timestamps.remove(0);
53 }
54 }
55
56 pub fn record_outgoing_path_request(&mut self, now: f64) {
58 self.op_timestamps.push(now);
59 if self.op_timestamps.len() > PATH_REQUEST_SAMPLE_MAX {
60 self.op_timestamps.remove(0);
61 }
62 }
63
64 fn compute_frequency(timestamps: &[f64], min_sample: usize) -> f64 {
66 let sample_count = timestamps.len();
67 if sample_count <= min_sample {
68 return 0.0;
69 }
70 let span = timestamps[sample_count - 1] - timestamps[0];
71 if span <= 0.0 {
72 return 0.0;
73 }
74 sample_count as f64 / span
75 }
76
77 pub fn incoming_announce_freq(&self) -> f64 {
79 Self::compute_frequency(&self.ia_timestamps, INCOMING_ANNOUNCE_MIN_SAMPLE)
80 }
81
82 pub fn outgoing_announce_freq(&self) -> f64 {
84 Self::compute_frequency(&self.oa_timestamps, 1)
85 }
86
87 pub fn incoming_path_request_freq(&self) -> f64 {
89 Self::compute_frequency(&self.ip_timestamps, INCOMING_PATH_REQUEST_MIN_SAMPLE)
90 }
91
92 pub fn outgoing_path_request_freq(&self) -> f64 {
94 Self::compute_frequency(&self.op_timestamps, 1)
95 }
96
97 pub fn outgoing_path_request_samples(&self) -> usize {
99 self.op_timestamps.len()
100 }
101}
102
103#[cfg(test)]
104mod tests {
105 use super::*;
106
107 #[test]
108 fn incoming_frequency_waits_for_minimum_sample_count() {
109 let mut stats = InterfaceStats::default();
110
111 for i in 0..INCOMING_ANNOUNCE_MIN_SAMPLE {
112 stats.record_incoming_announce(i as f64);
113 }
114
115 assert_eq!(
116 stats.incoming_announce_freq(),
117 0.0,
118 "incoming announce frequency must stay zero until more than the minimum samples exist"
119 );
120 }
121
122 #[test]
123 fn announce_frequency_keeps_bounded_samples() {
124 let mut stats = InterfaceStats::default();
125
126 for i in 0..ANNOUNCE_SAMPLE_MAX {
127 stats.record_incoming_announce(i as f64);
128 stats.record_outgoing_announce(i as f64);
129 }
130
131 assert_eq!(stats.ia_timestamps.len(), ANNOUNCE_SAMPLE_MAX);
132 assert_eq!(stats.oa_timestamps.len(), ANNOUNCE_SAMPLE_MAX);
133
134 stats.record_incoming_announce(ANNOUNCE_SAMPLE_MAX as f64);
135 stats.record_outgoing_announce(ANNOUNCE_SAMPLE_MAX as f64);
136
137 assert_eq!(stats.ia_timestamps.len(), ANNOUNCE_SAMPLE_MAX);
138 assert_eq!(stats.oa_timestamps.len(), ANNOUNCE_SAMPLE_MAX);
139 assert_eq!(stats.ia_timestamps[0], 1.0);
140 assert_eq!(stats.oa_timestamps[0], 1.0);
141 }
142
143 #[test]
144 fn incoming_frequency_uses_sample_count_over_oldest_span() {
145 let mut stats = InterfaceStats::default();
146
147 for i in 0..12 {
148 stats.record_incoming_announce(i as f64);
149 }
150
151 let expected = 12.0 / 11.0;
152 assert!(
153 (stats.incoming_announce_freq() - expected).abs() < f64::EPSILON,
154 "incoming frequency should be samples / span, got {} expected {}",
155 stats.incoming_announce_freq(),
156 expected
157 );
158 }
159
160 #[test]
161 fn path_request_frequency_tracks_incoming_and_outgoing_samples() {
162 let mut stats = InterfaceStats::default();
163
164 for i in 0..=INCOMING_PATH_REQUEST_MIN_SAMPLE {
165 stats.record_incoming_path_request(i as f64);
166 }
167 stats.record_outgoing_path_request(10.0);
168 stats.record_outgoing_path_request(12.0);
169
170 assert_eq!(stats.incoming_path_request_freq(), 3.0 / 2.0);
171 assert_eq!(stats.outgoing_path_request_freq(), 2.0 / 2.0);
172 assert_eq!(stats.outgoing_path_request_samples(), 2);
173 }
174
175 #[test]
176 fn path_request_frequency_keeps_bounded_samples() {
177 let mut stats = InterfaceStats::default();
178
179 for i in 0..PATH_REQUEST_SAMPLE_MAX {
180 stats.record_incoming_path_request(i as f64);
181 stats.record_outgoing_path_request(i as f64);
182 }
183
184 stats.record_incoming_path_request(PATH_REQUEST_SAMPLE_MAX as f64);
185 stats.record_outgoing_path_request(PATH_REQUEST_SAMPLE_MAX as f64);
186
187 assert_eq!(stats.ip_timestamps.len(), PATH_REQUEST_SAMPLE_MAX);
188 assert_eq!(stats.op_timestamps.len(), PATH_REQUEST_SAMPLE_MAX);
189 assert_eq!(stats.ip_timestamps[0], 1.0);
190 assert_eq!(stats.op_timestamps[0], 1.0);
191 }
192}