1use super::protocol::{control, slot, ARG0_WORD, OPCODE_WORD, STATUS_WORD, TENANT_WORD};
8use super::scaling::{
9 MegakernelLaunchPolicy, MegakernelLaunchRecommendation, MegakernelLaunchRequest,
10 PriorityRequeueAccounting,
11};
12use super::staging_reserve::{
13 reserve_hash_map_capacity, reserve_vec_capacity as reserve_target_capacity,
14};
15use crate::PipelineError;
16
17mod sketch;
18mod types;
19mod errors;
20pub use sketch::{CountMinSketch, SketchTelemetry, SketchTelemetryScratch};
21use types::WindowAccumulator;
22pub use types::{
23 ControlSnapshot, MegakernelRuntimeCounters, MegakernelRuntimeEvidence,
24 MegakernelWatchdogSnapshot, RingOccupancy, RingSlotSnapshot, RingStatus, RingTelemetry,
25 RuntimeEvidenceMetricCoverage, RuntimeEvidenceMetricFamily, TelemetryDecodeCapacityEvidence,
26 TelemetryDecodeScratch, WindowTelemetry, RUNTIME_IO_EVIDENCE_SCHEMA_VERSION,
27 TELEMETRY_DECODE_CAPACITY_SCHEMA_VERSION,
28};
29
30const SLOT_WORDS_USIZE: usize = 16;
31
32fn read_word(buf: &[u8], word_idx: usize) -> Option<u32> {
33 let off = word_idx.checked_mul(4)?;
34 let end = off.checked_add(4)?;
35 let bytes = buf.get(off..end)?;
36 Some(u32::from_le_bytes(bytes.try_into().ok()?))
37}
38
39fn try_read_slot_chunk_word(slot_bytes: &[u8], word_idx: u32) -> Result<u32, PipelineError> {
40 let word_idx = telemetry_u32_to_usize(word_idx, "slot word index")?;
41 let off = word_idx.checked_mul(4).ok_or_else(|| {
42 errors::slot_word_offset_overflow()
43 })?;
44 let end = off.checked_add(4).ok_or_else(|| {
45 errors::slot_word_end_overflow()
46 })?;
47 let bytes = slot_bytes.get(off..end).ok_or_else(|| {
48 errors::missing_slot_word(word_idx, slot_bytes.len())
49 })?;
50 Ok(u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
51}
52
53fn is_sorted_unique_u32(values: &[u32]) -> bool {
54 values.windows(2).all(|pair| pair[0] < pair[1])
55}
56
57impl ControlSnapshot {
58 #[must_use]
60 #[cfg(any(test, feature = "legacy-infallible"))]
61 pub fn decode(control_bytes: &[u8]) -> Self {
62 match Self::try_decode(control_bytes) {
63 Ok(snapshot) => snapshot,
64 Err(_) => Self::default(),
65 }
66 }
67
68 #[cfg(any(test, feature = "legacy-infallible"))]
70 pub fn decode_into(control_bytes: &[u8], out: &mut Self) {
71 if let Err(error) = Self::try_decode_into(control_bytes, out) {
75 panic!("vyre-runtime telemetry control-buffer decode failed: {error}");
76 }
77 }
78
79 pub fn try_decode(control_bytes: &[u8]) -> Result<Self, PipelineError> {
86 let mut out = Self::default();
87 Self::try_decode_into(control_bytes, &mut out)?;
88 Ok(out)
89 }
90
91 pub fn try_decode_into(control_bytes: &[u8], out: &mut Self) -> Result<(), PipelineError> {
98 validate_control_snapshot(control_bytes)?;
99 out.shutdown =
100 read_required_control_word(control_bytes, control_word_index(control::SHUTDOWN)?)? != 0;
101 out.done_count =
102 read_required_control_word(control_bytes, control_word_index(control::DONE_COUNT)?)?;
103 out.epoch = read_required_control_word(control_bytes, control_word_index(control::EPOCH)?)?;
104 out.metrics.clear();
105 reserve_target_capacity(
106 &mut out.metrics,
107 telemetry_u32_to_usize(control::METRICS_SLOTS, "metrics slot count")?,
108 "metrics",
109 )?;
110 for i in 0..control::METRICS_SLOTS {
111 let count = read_required_control_word(
112 control_bytes,
113 control_offset_index(control::METRICS_BASE, i)?,
114 )?;
115 if count > 0 {
116 out.metrics.push((i, count));
117 }
118 }
119 out.tenant_fairness.clear();
120 reserve_target_capacity(
121 &mut out.tenant_fairness,
122 telemetry_u32_to_usize(control::TENANT_FAIRNESS_SLOTS, "tenant fairness slot count")?,
123 "tenant fairness",
124 )?;
125 for i in 0..control::TENANT_FAIRNESS_SLOTS {
126 out.tenant_fairness.push(read_required_control_word(
127 control_bytes,
128 control_offset_index(control::TENANT_FAIRNESS_BASE, i)?,
129 )?);
130 }
131 out.priority_fairness.clear();
132 reserve_target_capacity(
133 &mut out.priority_fairness,
134 telemetry_u32_to_usize(
135 control::PRIORITY_FAIRNESS_SLOTS,
136 "priority fairness slot count",
137 )?,
138 "priority fairness",
139 )?;
140 for i in 0..control::PRIORITY_FAIRNESS_SLOTS {
141 out.priority_fairness.push(read_required_control_word(
142 control_bytes,
143 control_offset_index(control::PRIORITY_FAIRNESS_BASE, i)?,
144 )?);
145 }
146 Ok(())
147 }
148}
149
150impl RingTelemetry {
151 #[must_use]
153 #[cfg(any(test, feature = "legacy-infallible"))]
154 pub fn decode(control_bytes: &[u8], ring_bytes: &[u8]) -> Self {
155 Self::decode_with_window_opcodes(control_bytes, ring_bytes, &[])
156 }
157
158 pub fn try_decode(control_bytes: &[u8], ring_bytes: &[u8]) -> Result<Self, PipelineError> {
165 Self::try_decode_with_window_opcodes(control_bytes, ring_bytes, &[])
166 }
167
168 #[must_use]
172 #[cfg(any(test, feature = "legacy-infallible"))]
173 pub fn decode_with_window_opcodes(
174 control_bytes: &[u8],
175 ring_bytes: &[u8],
176 window_opcodes: &[u32],
177 ) -> Self {
178 match Self::try_decode_with_window_opcodes(control_bytes, ring_bytes, window_opcodes) {
179 Ok(telemetry) => telemetry,
180 Err(_) => Self::default(),
181 }
182 }
183
184 #[cfg(any(test, feature = "legacy-infallible"))]
187 pub fn decode_with_window_opcodes_into(
188 control_bytes: &[u8],
189 ring_bytes: &[u8],
190 window_opcodes: &[u32],
191 out: &mut Self,
192 scratch: &mut TelemetryDecodeScratch,
193 ) {
194 Self::try_decode_with_window_opcodes_into(
195 control_bytes,
196 ring_bytes,
197 window_opcodes,
198 out,
199 scratch,
200 )
201 .unwrap_or_else(|_| {
202 *out = Self::default();
203 scratch.clear();
204 });
205 }
206
207 fn try_decode_with_window_opcodes_into_unchecked(
208 control_bytes: &[u8],
209 ring_bytes: &[u8],
210 window_opcodes: &[u32],
211 out: &mut Self,
212 scratch: &mut TelemetryDecodeScratch,
213 ) -> Result<(), PipelineError> {
214 enum WindowOpcodeMatcher<'a> {
215 None,
216 Single(u32),
217 DenseBitmap(u128),
218 SmallSlice(&'a [u32]),
219 LargeSlice(&'a [u32]),
220 }
221
222 ControlSnapshot::try_decode_into(control_bytes, &mut out.control)?;
223 let slot_count = ring_bytes.len() / slot_byte_len()?;
224 out.occupancy = RingOccupancy::default();
225 out.slots.clear();
226 reserve_target_capacity(&mut out.slots, slot_count, "ring slots")?;
227 out.windows.clear();
228 scratch.window_opcodes.clear();
229 scratch.windows.clear();
230 let window_opcode_lookup = if window_opcodes.is_empty() {
231 &[][..]
232 } else if is_sorted_unique_u32(window_opcodes) {
233 window_opcodes
234 } else {
235 reserve_target_capacity(
236 &mut scratch.window_opcodes,
237 window_opcodes.len(),
238 "window opcode scratch",
239 )?;
240 scratch.window_opcodes.extend_from_slice(window_opcodes);
241 scratch.window_opcodes.sort_unstable();
242 scratch.window_opcodes.dedup();
243 scratch.window_opcodes.as_slice()
244 };
245 let window_opcode_matcher = match window_opcode_lookup {
246 [] => WindowOpcodeMatcher::None,
247 [opcode] => WindowOpcodeMatcher::Single(*opcode),
248 opcodes if opcodes.len() > 1 && opcodes.iter().all(|opcode| *opcode < 128) => {
249 let bitmap = opcodes
250 .iter()
251 .fold(0_u128, |acc, &opcode| acc | (1_u128 << opcode));
252 WindowOpcodeMatcher::DenseBitmap(bitmap)
253 }
254 opcodes if opcodes.len() <= 8 => WindowOpcodeMatcher::SmallSlice(opcodes),
255 opcodes => WindowOpcodeMatcher::LargeSlice(opcodes),
256 };
257 if !matches!(window_opcode_matcher, WindowOpcodeMatcher::None) {
258 reserve_hash_map_capacity(
259 &mut scratch.windows,
260 slot_count,
261 "window accumulator scratch",
262 )?;
263 }
264 let decode_windows = !matches!(window_opcode_matcher, WindowOpcodeMatcher::None);
265
266 let slot_byte_len = slot_byte_len()?;
267 for (slot_idx, slot_bytes) in ring_bytes.chunks_exact(slot_byte_len).enumerate() {
268 let slot_idx = u32::try_from(slot_idx).map_err(|source| {
269 PipelineError::Backend(format!(
270 "megakernel telemetry slot index cannot fit u32: {source}. Fix: shard ring snapshots before host decode."
271 ))
272 })?;
273 let status_raw = try_read_slot_chunk_word(slot_bytes, STATUS_WORD)?;
274 let status = RingStatus::from_raw(status_raw);
275 match status {
276 RingStatus::Empty => out.occupancy.empty += 1,
277 RingStatus::Published => out.occupancy.published += 1,
278 RingStatus::Claimed => out.occupancy.claimed += 1,
279 RingStatus::Done => out.occupancy.done += 1,
280 RingStatus::WaitIo => out.occupancy.wait_io += 1,
281 RingStatus::Yield => out.occupancy.yield_count += 1,
282 RingStatus::Requeue => out.occupancy.requeue += 1,
283 RingStatus::Fault => out.occupancy.fault += 1,
284 RingStatus::Unknown(_) => out.occupancy.unknown += 1,
285 }
286 let tenant_id = try_read_slot_chunk_word(slot_bytes, TENANT_WORD)?;
287 let opcode = try_read_slot_chunk_word(slot_bytes, OPCODE_WORD)?;
288 let args_prefix = [
289 try_read_slot_chunk_word(slot_bytes, ARG0_WORD)?,
290 try_read_slot_chunk_word(slot_bytes, ARG0_WORD + 1)?,
291 try_read_slot_chunk_word(slot_bytes, ARG0_WORD + 2)?,
292 ];
293 let is_window_opcode = match window_opcode_matcher {
294 WindowOpcodeMatcher::None => false,
295 WindowOpcodeMatcher::Single(expected) => opcode == expected,
296 WindowOpcodeMatcher::DenseBitmap(bitmap) => {
297 opcode < 128 && ((bitmap >> opcode) & 1) == 1
298 }
299 WindowOpcodeMatcher::SmallSlice(window_opcodes) => window_opcodes.contains(&opcode),
300 WindowOpcodeMatcher::LargeSlice(window_opcodes) => {
301 window_opcodes.binary_search(&opcode).is_ok()
302 }
303 };
304 if decode_windows && is_window_opcode {
305 let ticket = args_prefix[0];
306 let class_tag = args_prefix[1];
307 let entry =
308 scratch
309 .windows
310 .entry((ticket, opcode))
311 .or_insert_with(|| WindowAccumulator {
312 tenant_id,
313 opcode,
314 ..WindowAccumulator::default()
315 });
316 match class_tag {
317 0 => entry.required_slots += 1,
318 1 => entry.lookahead_slots += 1,
319 _ => {}
320 }
321 match status {
322 RingStatus::Published => entry.published += 1,
323 RingStatus::Claimed => entry.claimed += 1,
324 RingStatus::Done => entry.done += 1,
325 RingStatus::WaitIo => entry.wait_io += 1,
326 RingStatus::Yield => entry.yield_count += 1,
327 RingStatus::Requeue => entry.requeue += 1,
328 RingStatus::Fault => entry.fault += 1,
329 RingStatus::Empty | RingStatus::Unknown(_) => {}
330 }
331 }
332 out.slots.push(RingSlotSnapshot {
333 slot_idx,
334 status,
335 tenant_id,
336 opcode,
337 args_prefix,
338 });
339 }
340
341 reserve_target_capacity(&mut out.windows, scratch.windows.len(), "window output")?;
342 for (&(ticket, _), acc) in &scratch.windows {
343 out.windows.push(WindowTelemetry {
344 ticket,
345 tenant_id: acc.tenant_id,
346 opcode: acc.opcode,
347 required_slots: acc.required_slots,
348 lookahead_slots: acc.lookahead_slots,
349 published: acc.published,
350 claimed: acc.claimed,
351 done: acc.done,
352 wait_io: acc.wait_io,
353 yield_count: acc.yield_count,
354 requeue: acc.requeue,
355 fault: acc.fault,
356 });
357 }
358 out.windows
359 .sort_unstable_by_key(|window| (window.ticket, window.opcode));
360 Ok(())
361 }
362
363 pub fn try_decode_with_window_opcodes(
370 control_bytes: &[u8],
371 ring_bytes: &[u8],
372 window_opcodes: &[u32],
373 ) -> Result<Self, PipelineError> {
374 validate_telemetry_buffers(control_bytes, ring_bytes)?;
375 let mut out = Self::default();
376 let mut scratch = TelemetryDecodeScratch::new();
377 Self::try_decode_with_window_opcodes_into_unchecked(
378 control_bytes,
379 ring_bytes,
380 window_opcodes,
381 &mut out,
382 &mut scratch,
383 )?;
384 Ok(out)
385 }
386
387 pub fn try_decode_with_window_opcodes_into(
395 control_bytes: &[u8],
396 ring_bytes: &[u8],
397 window_opcodes: &[u32],
398 out: &mut Self,
399 scratch: &mut TelemetryDecodeScratch,
400 ) -> Result<(), PipelineError> {
401 validate_telemetry_buffers(control_bytes, ring_bytes)?;
402 Self::try_decode_with_window_opcodes_into_unchecked(
403 control_bytes,
404 ring_bytes,
405 window_opcodes,
406 out,
407 scratch,
408 )?;
409 Ok(())
410 }
411
412 #[must_use]
414 #[cfg(any(test, feature = "legacy-infallible"))]
415 pub fn active_slots_for_opcode(&self, opcode: u32) -> Vec<&RingSlotSnapshot> {
416 match self.try_active_slots_for_opcode(opcode) {
417 Ok(slots) => slots,
418 Err(_) => Vec::default(),
419 }
420 }
421
422 pub fn try_active_slots_for_opcode(
428 &self,
429 opcode: u32,
430 ) -> Result<Vec<&RingSlotSnapshot>, PipelineError> {
431 let mut out = Vec::default();
432 self.try_active_slots_for_opcode_into(opcode, &mut out)?;
433 Ok(out)
434 }
435
436 pub fn active_slots_for_opcode_iter(
438 &self,
439 opcode: u32,
440 ) -> impl Iterator<Item = &RingSlotSnapshot> {
441 self.slots
442 .iter()
443 .filter(move |slot| slot.opcode == opcode && slot.status.is_active())
444 }
445
446 #[cfg(any(test, feature = "legacy-infallible"))]
448 pub fn active_slots_for_opcode_into<'a>(
449 &'a self,
450 opcode: u32,
451 out: &mut Vec<&'a RingSlotSnapshot>,
452 ) {
453 if let Err(error) = self.try_active_slots_for_opcode_into(opcode, out) {
457 panic!("vyre-runtime telemetry active-slots readback failed: {error}");
458 }
459 }
460
461 pub fn try_active_slots_for_opcode_into<'a>(
467 &'a self,
468 opcode: u32,
469 out: &mut Vec<&'a RingSlotSnapshot>,
470 ) -> Result<(), PipelineError> {
471 out.clear();
472 reserve_target_capacity(out, self.slots.len(), "active slot output")?;
473 self.slots
474 .iter()
475 .filter(|slot| slot.opcode == opcode && slot.status.is_active())
476 .for_each(|slot| out.push(slot));
477 Ok(())
478 }
479
480 #[must_use]
482 #[cfg(any(test, feature = "legacy-infallible"))]
483 pub fn active_windows(&self) -> Vec<&WindowTelemetry> {
484 match self.try_active_windows() {
485 Ok(windows) => windows,
486 Err(_) => Vec::default(),
487 }
488 }
489
490 pub fn try_active_windows(&self) -> Result<Vec<&WindowTelemetry>, PipelineError> {
496 let mut out = Vec::default();
497 self.try_active_windows_into(&mut out)?;
498 Ok(out)
499 }
500
501 #[cfg(any(test, feature = "legacy-infallible"))]
503 pub fn active_windows_into<'a>(&'a self, out: &mut Vec<&'a WindowTelemetry>) {
504 if let Err(error) = self.try_active_windows_into(out) {
508 panic!("vyre-runtime telemetry active-windows readback failed: {error}");
509 }
510 }
511
512 pub fn try_active_windows_into<'a>(
518 &'a self,
519 out: &mut Vec<&'a WindowTelemetry>,
520 ) -> Result<(), PipelineError> {
521 out.clear();
522 reserve_target_capacity(out, self.windows.len(), "active window output")?;
523 self.windows
524 .iter()
525 .filter(|window| window.is_active())
526 .for_each(|window| out.push(window));
527 Ok(())
528 }
529
530 #[must_use]
532 pub fn priority_accounting(&self) -> PriorityRequeueAccounting {
533 PriorityRequeueAccounting {
534 requeue_count: u64::from(self.occupancy.requeue),
535 aged_promotions: 0,
536 max_priority_age: 0,
537 }
538 }
539
540 #[must_use]
543 #[cfg(any(test, feature = "legacy-infallible"))]
544 pub fn runtime_counters(&self) -> MegakernelRuntimeCounters {
545 match self.try_runtime_counters() {
546 Ok(counters) => counters,
547 Err(_) => zero_runtime_counters(),
548 }
549 }
550
551 pub fn try_runtime_counters(&self) -> Result<MegakernelRuntimeCounters, PipelineError> {
558 let total_slots = self.occupancy.total_slots();
559 let queue_depth = self.occupancy.queue_depth();
560 let gpu_idle_slots = self.occupancy.empty;
561 let gpu_idle_ppm = if total_slots == 0 {
562 0
563 } else {
564 let raw_idle_ppm = (u64::from(gpu_idle_slots) * 1_000_000) / u64::from(total_slots);
565 raw_idle_ppm.min(1_000_000) as u32
566 };
567 let frontier_density_bps = try_density_bps(queue_depth, total_slots)?;
568 let active_slots = total_slots.saturating_sub(gpu_idle_slots);
569 let occupancy_proxy_bps = try_density_bps(active_slots, total_slots)?;
570 let tenant_fairness_total = try_sum_u32_as_u64(
571 &self.control.tenant_fairness,
572 "tenant fairness total",
573 "shard tenant counters before telemetry aggregation",
574 )?;
575 let priority_fairness_total = try_sum_u32_as_u64(
576 &self.control.priority_fairness,
577 "priority fairness total",
578 "shard priority counters before telemetry aggregation",
579 )?;
580 let tenant_fairness_skew = try_fairness_skew(&self.control.tenant_fairness)?;
581 Ok(MegakernelRuntimeCounters {
582 total_slots,
583 queue_depth,
584 gpu_idle_slots,
585 gpu_idle_ppm,
586 frontier_density_bps,
587 occupancy_proxy_bps,
588 drained_slots: self.control.done_count,
589 unreclaimed_done_slots: self.occupancy.done,
590 tenant_fairness_total,
591 tenant_fairness_skew,
592 priority_fairness_total,
593 requeue_slots: self.occupancy.requeue,
594 fault_slots: self.occupancy.fault,
595 })
596 }
597
598 #[must_use]
601 #[cfg(any(test, feature = "legacy-infallible"))]
602 pub fn health_since(&self, previous: &RingTelemetry) -> MegakernelWatchdogSnapshot {
603 match self.try_health_since(previous) {
604 Ok(snapshot) => snapshot,
605 Err(_) => zero_watchdog_snapshot(),
606 }
607 }
608
609 pub fn try_health_since(
616 &self,
617 previous: &RingTelemetry,
618 ) -> Result<MegakernelWatchdogSnapshot, PipelineError> {
619 let counters = self.try_runtime_counters()?;
620 let done_delta = self
621 .control
622 .done_count
623 .checked_sub(previous.control.done_count)
624 .ok_or_else(|| {
625 errors::done_counter_backwards(previous.control.done_count, self.control.done_count)
626 })?;
627 let suspected_stall =
628 counters.queue_depth > 0 && done_delta == 0 && counters.fault_slots == 0;
629 Ok(MegakernelWatchdogSnapshot {
630 done_delta,
631 queue_depth: counters.queue_depth,
632 fault_slots: counters.fault_slots,
633 requeue_slots: counters.requeue_slots,
634 gpu_idle_ppm: counters.gpu_idle_ppm,
635 suspected_stall,
636 })
637 }
638
639 pub fn recommend_launch(
645 &self,
646 mut request: MegakernelLaunchRequest,
647 ) -> Result<MegakernelLaunchRecommendation, vyre_driver::BackendError> {
648 let counters = self
649 .try_runtime_counters()
650 .map_err(errors::launch_telemetry_failed)?;
651 if request.graph_node_count == 0 {
652 request.graph_node_count = counters.total_slots;
653 }
654 if request.graph_edge_count == 0 {
655 request.graph_edge_count = counters.queue_depth;
656 }
657 if request.frontier_density_bps == 0 {
658 request.frontier_density_bps = counters.frontier_density_bps;
659 }
660 request.hot_opcode_count = self
661 .control
662 .metrics
663 .iter()
664 .filter(|(_, count)| *count > 0)
665 .count()
666 .try_into()
667 .map_err(errors::hot_opcode_count_overflow)?;
668 let mut hot_window_count = 0usize;
669 for window in &self.windows {
670 let demand = window
671 .required_slots
672 .checked_add(window.lookahead_slots)
673 .ok_or_else(|| {
674 errors::route_window_demand_overflow()
675 })?;
676 if demand >= 4 {
677 hot_window_count = hot_window_count.checked_add(1).ok_or_else(|| {
678 errors::hot_window_count_overflow()
679 })?;
680 }
681 }
682 request.hot_window_count = hot_window_count
683 .try_into()
684 .map_err(errors::hot_window_count_too_wide)?;
685 request.requeue_count = request
686 .requeue_count
687 .checked_add(u64::from(self.occupancy.requeue))
688 .ok_or_else(errors::requeue_count_overflow)?;
689 MegakernelLaunchPolicy::standard().recommend(request)
690 }
691}
692
693#[cfg(any(test, feature = "legacy-infallible"))]
696fn zero_runtime_counters() -> MegakernelRuntimeCounters {
697 MegakernelRuntimeCounters {
698 total_slots: 0,
699 queue_depth: 0,
700 gpu_idle_slots: 0,
701 gpu_idle_ppm: 0,
702 frontier_density_bps: 0,
703 occupancy_proxy_bps: 0,
704 drained_slots: 0,
705 unreclaimed_done_slots: 0,
706 tenant_fairness_total: 0,
707 tenant_fairness_skew: 0,
708 priority_fairness_total: 0,
709 requeue_slots: 0,
710 fault_slots: 0,
711 }
712}
713
714#[cfg(any(test, feature = "legacy-infallible"))]
717fn zero_watchdog_snapshot() -> MegakernelWatchdogSnapshot {
718 MegakernelWatchdogSnapshot {
719 done_delta: 0,
720 queue_depth: 0,
721 fault_slots: 0,
722 requeue_slots: 0,
723 gpu_idle_ppm: 0,
724 suspected_stall: false,
725 }
726}
727
728fn read_required_control_word(control_bytes: &[u8], word_idx: usize) -> Result<u32, PipelineError> {
729 read_word(control_bytes, word_idx).ok_or_else(|| errors::missing_control_word(word_idx))
730}
731
732fn try_density_bps(numerator: u32, denominator: u32) -> Result<u16, PipelineError> {
733 if denominator == 0 {
734 return Ok(0);
735 }
736 let bps = (u64::from(numerator) * 10_000) / u64::from(denominator);
737 u16::try_from(bps.min(u64::from(u16::MAX))).map_err(errors::density_bps_overflow)
738}
739
740fn validate_telemetry_buffers(
741 control_bytes: &[u8],
742 ring_bytes: &[u8],
743) -> Result<(), PipelineError> {
744 validate_control_snapshot(control_bytes)?;
745 let slot_bytes = slot_byte_len()?;
746 if ring_bytes.len() % slot_bytes != 0 {
747 return Err(errors::ring_slot_alignment(ring_bytes.len(), slot_bytes));
748 }
749 let slot_count = ring_bytes.len() / slot_bytes;
750 if u32::try_from(slot_count).is_err() {
751 return Err(errors::ring_slot_count_too_wide(slot_count));
752 }
753 Ok(())
754}
755
756fn validate_control_snapshot(control_bytes: &[u8]) -> Result<(), PipelineError> {
757 let min_control = super::protocol::control_byte_len(0).ok_or_else(|| {
758 errors::control_length_overflow()
759 })?;
760 if control_bytes.len() < min_control || control_bytes.len() % 4 != 0 {
761 return Err(errors::bad_control_snapshot(
762 control_bytes.len(),
763 min_control,
764 ));
765 }
766 Ok(())
767}
768
769fn slot_byte_len() -> Result<usize, PipelineError> {
770 SLOT_WORDS_USIZE.checked_mul(4).ok_or_else(|| {
771 errors::slot_byte_width_overflow()
772 })
773}
774
775fn telemetry_u32_to_usize(value: u32, label: &'static str) -> Result<usize, PipelineError> {
776 usize::try_from(value).map_err(|source| errors::telemetry_u32_to_usize(value, label, source))
777}
778
779fn control_word_index(word: u32) -> Result<usize, PipelineError> {
780 usize::try_from(word).map_err(|source| errors::control_word_index(word, source))
781}
782
783fn control_offset_index(base: u32, offset: u32) -> Result<usize, PipelineError> {
784 let word = base.checked_add(offset).ok_or_else(|| {
785 errors::control_word_offset_overflow()
786 })?;
787 control_word_index(word)
788}
789
790fn try_sum_u32_as_u64(
791 counters: &[u32],
792 label: &'static str,
793 fix: &'static str,
794) -> Result<u64, PipelineError> {
795 counters.iter().try_fold(0u64, |acc, &count| {
796 acc.checked_add(u64::from(count)).ok_or_else(|| {
797 errors::counter_sum_overflow(label, fix)
798 })
799 })
800}
801
802fn try_fairness_skew(counters: &[u32]) -> Result<u32, PipelineError> {
803 let mut min_nonzero = u32::MAX;
804 let mut max = 0u32;
805 for &count in counters {
806 if count != 0 {
807 min_nonzero = min_nonzero.min(count);
808 max = max.max(count);
809 }
810 }
811 if min_nonzero == u32::MAX {
812 Ok(0)
813 } else {
814 max.checked_sub(min_nonzero).ok_or_else(|| {
815 errors::fairness_skew_invalid(max, min_nonzero)
816 })
817 }
818}
819
820#[cfg(test)]
821mod tests {
822 include!("telemetry_tests.rs");
823}