Skip to main content

prns_runtime/runtime/packet_phy_retention/
fixed.rs

1use 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}