1use std::sync::atomic::{AtomicU64, Ordering};
2
3#[derive(Debug, Default)]
8pub struct PeerPacketStats {
9 packets_out: AtomicU64,
10 packets_in: AtomicU64,
11 bytes_out: AtomicU64,
12 bytes_in: AtomicU64,
13}
14
15impl PeerPacketStats {
16 #[inline]
18 pub fn record_packet_out(&self, bytes: usize) {
19 self.packets_out.fetch_add(1, Ordering::Relaxed);
20 self.bytes_out.fetch_add(bytes as u64, Ordering::Relaxed);
21 }
22
23 #[inline]
25 pub fn record_packet_in(&self, bytes: usize) {
26 self.packets_in.fetch_add(1, Ordering::Relaxed);
27 self.bytes_in.fetch_add(bytes as u64, Ordering::Relaxed);
28 }
29
30 pub fn snapshot(&self) -> PeerPacketStatsSnapshot {
32 PeerPacketStatsSnapshot {
33 packets_out: self.packets_out.load(Ordering::Relaxed),
34 packets_in: self.packets_in.load(Ordering::Relaxed),
35 bytes_out: self.bytes_out.load(Ordering::Relaxed),
36 bytes_in: self.bytes_in.load(Ordering::Relaxed),
37 }
38 }
39}
40
41#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
45#[serde(rename_all = "camelCase")]
46pub struct PeerPacketStatsSnapshot {
47 pub packets_out: u64,
48 pub packets_in: u64,
49 pub bytes_out: u64,
50 pub bytes_in: u64,
51}
52
53#[cfg(test)]
54mod tests {
55 use super::*;
56
57 #[test]
58 fn peer_packet_stats_should_start_at_zero() {
59 let stats = PeerPacketStats::default();
60 let snapshot = stats.snapshot();
61
62 insta::assert_yaml_snapshot!(snapshot);
63 }
64
65 #[test]
66 fn peer_packet_stats_should_record_outgoing_packets() {
67 let stats = PeerPacketStats::default();
68
69 stats.record_packet_out(100);
70 stats.record_packet_out(200);
71
72 let snapshot = stats.snapshot();
73 insta::assert_yaml_snapshot!(snapshot);
74 }
75
76 #[test]
77 fn peer_packet_stats_should_record_incoming_packets() {
78 let stats = PeerPacketStats::default();
79
80 stats.record_packet_in(50);
81 stats.record_packet_in(150);
82 stats.record_packet_in(100);
83
84 let snapshot = stats.snapshot();
85 insta::assert_yaml_snapshot!(snapshot);
86 }
87
88 #[test]
89 fn peer_packet_stats_should_record_bidirectional_traffic() {
90 let stats = PeerPacketStats::default();
91
92 stats.record_packet_out(1000);
93 stats.record_packet_in(500);
94 stats.record_packet_out(2000);
95 stats.record_packet_in(1500);
96
97 let snapshot = stats.snapshot();
98 insta::assert_yaml_snapshot!(snapshot);
99 }
100}