subetha_cxc/
control_table.rs1use std::sync::atomic::{AtomicU8, Ordering};
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20#[repr(u8)]
21pub enum CodingLevel {
22 Passthrough = 0,
24 Fec = 1,
26 Interleave = 2,
28 Salvage = 3,
30}
31
32impl CodingLevel {
33 #[inline]
35 pub fn from_u8(v: u8) -> Self {
36 match v {
37 0 => CodingLevel::Passthrough,
38 1 => CodingLevel::Fec,
39 2 => CodingLevel::Interleave,
40 _ => CodingLevel::Salvage,
41 }
42 }
43}
44
45#[derive(Debug)]
48pub struct ControlTable {
49 level: AtomicU8,
50 parity_r: AtomicU8,
51 interleave_depth: AtomicU8,
52 inner_fec: AtomicU8,
53 tower_depth: AtomicU8,
54}
55
56impl Default for ControlTable {
57 fn default() -> Self {
58 Self {
61 level: AtomicU8::new(CodingLevel::Fec as u8),
62 parity_r: AtomicU8::new(2),
63 interleave_depth: AtomicU8::new(1),
64 inner_fec: AtomicU8::new(0),
65 tower_depth: AtomicU8::new(0),
66 }
67 }
68}
69
70impl ControlTable {
71 pub fn new() -> Self {
73 Self::default()
74 }
75
76 #[inline]
80 pub fn level(&self) -> CodingLevel {
81 CodingLevel::from_u8(self.level.load(Ordering::Relaxed))
82 }
83
84 #[inline]
86 pub fn parity_r(&self) -> u8 {
87 self.parity_r.load(Ordering::Relaxed)
88 }
89
90 #[inline]
92 pub fn interleave_depth(&self) -> u8 {
93 self.interleave_depth.load(Ordering::Relaxed).max(1)
94 }
95
96 #[inline]
98 pub fn inner_fec(&self) -> bool {
99 self.inner_fec.load(Ordering::Relaxed) != 0
100 }
101
102 #[inline]
104 pub fn tower_depth(&self) -> u8 {
105 self.tower_depth.load(Ordering::Relaxed)
106 }
107
108 pub fn set_level(&self, level: CodingLevel) {
112 self.level.store(level as u8, Ordering::Relaxed);
113 }
114
115 pub fn set_parity_r(&self, r: u8) {
117 self.parity_r.store(r, Ordering::Relaxed);
118 }
119
120 pub fn set_interleave_depth(&self, d: u8) {
122 self.interleave_depth.store(d.max(1), Ordering::Relaxed);
123 }
124
125 pub fn set_inner_fec(&self, on: bool) {
127 self.inner_fec.store(on as u8, Ordering::Relaxed);
128 }
129
130 pub fn set_tower_depth(&self, d: u8) {
132 self.tower_depth.store(d, Ordering::Relaxed);
133 }
134}
135
136#[cfg(test)]
137mod tests {
138 use super::*;
139 use std::sync::Arc;
140
141 #[test]
142 fn defaults_are_clean_link() {
143 let t = ControlTable::new();
144 assert_eq!(t.level(), CodingLevel::Fec);
145 assert_eq!(t.parity_r(), 2);
146 assert_eq!(t.interleave_depth(), 1);
147 assert!(!t.inner_fec());
148 assert_eq!(t.tower_depth(), 0);
149 }
150
151 #[test]
152 fn writes_are_visible_to_reads() {
153 let t = ControlTable::new();
154 t.set_level(CodingLevel::Interleave);
155 t.set_parity_r(4);
156 t.set_interleave_depth(8);
157 t.set_inner_fec(true);
158 t.set_tower_depth(1);
159 assert_eq!(t.level(), CodingLevel::Interleave);
160 assert_eq!(t.parity_r(), 4);
161 assert_eq!(t.interleave_depth(), 8);
162 assert!(t.inner_fec());
163 assert_eq!(t.tower_depth(), 1);
164 }
165
166 #[test]
167 fn interleave_depth_floor_is_one() {
168 let t = ControlTable::new();
169 t.set_interleave_depth(0);
170 assert_eq!(t.interleave_depth(), 1);
171 }
172
173 #[test]
174 fn shared_across_threads() {
175 let t = Arc::new(ControlTable::new());
178 let writer = {
179 let t = Arc::clone(&t);
180 std::thread::spawn(move || {
181 for i in 0..10_000u32 {
182 t.set_level(CodingLevel::from_u8((i % 4) as u8));
183 }
184 })
185 };
186 let mut seen = 0u32;
187 for _ in 0..10_000 {
188 if std::hint::black_box(t.level()) == CodingLevel::Passthrough {
190 seen += 1;
191 }
192 }
193 writer.join().unwrap();
194 std::hint::black_box(seen);
197 }
198}