prns_runtime/runtime/packet_phy_retention/
fixed.rs1use core::num::NonZeroUsize;
2
3use heapless::Vec as HeaplessVec;
4use prns_core::lemire_index::LemireIndex;
5
6use crate::interfaces::{RssiDbm, SignalQualityTenthsPercent, SnrQuarterDb};
7use crate::routing::dedup::{dedup_index_buckets, PacketHash};
8
9use super::{PacketMetricStorage, PacketPhyRetention};
10
11pub struct FixedPacketMetricStorage<Metric, const CAPACITY: usize, const BUCKETS: usize> {
12 packet_hashes: HeaplessVec<PacketHash, CAPACITY>,
13 metrics: HeaplessVec<Metric, CAPACITY>,
14 index: LemireIndex<BUCKETS>,
15}
16
17impl<Metric, const CAPACITY: usize, const BUCKETS: usize>
18 FixedPacketMetricStorage<Metric, CAPACITY, BUCKETS>
19{
20 pub const fn new() -> Self {
21 const {
22 assert!(
23 CAPACITY > 0,
24 "fixed packet PHY retention capacity must be non-zero"
25 );
26 assert!(
27 CAPACITY < u16::MAX as usize,
28 "fixed packet PHY retention exceeds its index slot range"
29 );
30 assert!(
31 BUCKETS >= dedup_index_buckets(CAPACITY),
32 "fixed packet PHY index needs two-thirds-load headroom"
33 );
34 }
35 Self {
36 packet_hashes: HeaplessVec::new(),
37 metrics: HeaplessVec::new(),
38 index: LemireIndex::new(),
39 }
40 }
41}
42
43impl<Metric, const CAPACITY: usize, const BUCKETS: usize> Default
44 for FixedPacketMetricStorage<Metric, CAPACITY, BUCKETS>
45{
46 fn default() -> Self {
47 Self::new()
48 }
49}
50
51impl<Metric: Copy, const CAPACITY: usize, const BUCKETS: usize> PacketMetricStorage
52 for FixedPacketMetricStorage<Metric, CAPACITY, BUCKETS>
53{
54 type Metric = Metric;
55
56 fn capacity(&self) -> NonZeroUsize {
57 match NonZeroUsize::new(CAPACITY) {
58 Some(capacity) => capacity,
59 None => unreachable!("fixed packet PHY retention capacity is non-zero"),
60 }
61 }
62
63 fn len(&self) -> usize {
64 self.packet_hashes.len()
65 }
66
67 fn append(&mut self, packet_hash: PacketHash, metric: Metric) {
68 let slot = self.packet_hashes.len();
69 assert!(
70 self.packet_hashes.push(packet_hash).is_ok(),
71 "shared packet PHY retention exceeded fixed hash capacity"
72 );
73 assert!(
74 self.metrics.push(metric).is_ok(),
75 "shared packet PHY retention exceeded fixed metric capacity"
76 );
77 self.index.insert(slot, &self.packet_hashes);
78 }
79
80 fn replace(&mut self, slot: usize, packet_hash: PacketHash, metric: Metric) {
81 self.index.remove_slot(slot, &self.packet_hashes);
82 self.packet_hashes[slot] = packet_hash;
83 self.metrics[slot] = metric;
84 self.index.insert(slot, &self.packet_hashes);
85 }
86
87 fn get(&self, packet_hash: PacketHash) -> Option<Metric> {
88 self.index
89 .get(&packet_hash, &self.packet_hashes)
90 .map(|slot| self.metrics[slot])
91 }
92}
93
94pub type FixedPacketPhyRetention<const CAPACITY: usize, const BUCKETS: usize> = PacketPhyRetention<
95 FixedPacketMetricStorage<RssiDbm, CAPACITY, BUCKETS>,
96 FixedPacketMetricStorage<SnrQuarterDb, CAPACITY, BUCKETS>,
97 FixedPacketMetricStorage<SignalQualityTenthsPercent, CAPACITY, BUCKETS>,
98>;
99
100pub const fn fixed_packet_phy_retention<const CAPACITY: usize, const BUCKETS: usize>(
101) -> FixedPacketPhyRetention<CAPACITY, BUCKETS> {
102 PacketPhyRetention::from_storages(
103 FixedPacketMetricStorage::new(),
104 FixedPacketMetricStorage::new(),
105 FixedPacketMetricStorage::new(),
106 )
107}