1use super::protocol::{control, read_word, slot, ARG0_WORD, OPCODE_WORD, STATUS_WORD, TENANT_WORD};
8use super::scaling::{
9 PriorityRequeueAccounting, ResidentLaunchPolicy, ResidentLaunchRecommendation,
10 ResidentLaunchRequest,
11};
12use super::staging_reserve::{
13 reserve_hash_map_capacity, reserve_vec_capacity as reserve_target_capacity,
14};
15use crate::PipelineError;
16
17mod errors;
18mod evidence;
19mod ring_state;
20mod sketch;
21pub use evidence::{
22 ResidentRuntimeEvidence, RuntimeEvidenceMetricCoverage, RuntimeEvidenceMetricFamily,
23 TelemetryDecodeCapacityEvidence, RUNTIME_IO_EVIDENCE_SCHEMA_VERSION,
24 TELEMETRY_DECODE_CAPACITY_SCHEMA_VERSION,
25};
26use ring_state::WindowAccumulator;
27pub use ring_state::{
28 ControlSnapshot, ResidentRuntimeCounters, ResidentWatchdogSnapshot, RingOccupancy,
29 RingSlotSnapshot, RingStatus, RingTelemetry, TelemetryDecodeScratch, WindowTelemetry,
30};
31pub use sketch::{CountMinSketch, SketchTelemetry, SketchTelemetryScratch};
32
33const SLOT_WORDS_USIZE: usize = 16;
34
35fn try_read_slot_chunk_word(slot_bytes: &[u8], word_idx: u32) -> Result<u32, PipelineError> {
36 let word_idx = telemetry_u32_to_usize(word_idx, "slot word index")?;
37 let off = word_idx
38 .checked_mul(4)
39 .ok_or_else(|| errors::slot_word_offset_overflow())?;
40 let end = off
41 .checked_add(4)
42 .ok_or_else(|| errors::slot_word_end_overflow())?;
43 let bytes = slot_bytes
44 .get(off..end)
45 .ok_or_else(|| errors::missing_slot_word(word_idx, slot_bytes.len()))?;
46 Ok(u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
47}
48
49fn is_sorted_unique_u32(values: &[u32]) -> bool {
50 values.windows(2).all(|pair| pair[0] < pair[1])
51}
52
53impl ControlSnapshot {
54 pub fn try_decode(control_bytes: &[u8]) -> Result<Self, PipelineError> {
61 let mut out = Self::default();
62 Self::try_decode_into(control_bytes, &mut out)?;
63 Ok(out)
64 }
65
66 pub fn try_decode_into(control_bytes: &[u8], out: &mut Self) -> Result<(), PipelineError> {
73 validate_control_snapshot(control_bytes)?;
74 out.shutdown =
75 read_required_control_word(control_bytes, control_word_index(control::SHUTDOWN)?)? != 0;
76 out.done_count =
77 read_required_control_word(control_bytes, control_word_index(control::DONE_COUNT)?)?;
78 out.epoch = read_required_control_word(control_bytes, control_word_index(control::EPOCH)?)?;
79 out.metrics.clear();
80 reserve_target_capacity(
81 &mut out.metrics,
82 telemetry_u32_to_usize(control::METRICS_SLOTS, "metrics slot count")?,
83 "metrics",
84 )?;
85 for i in 0..control::METRICS_SLOTS {
86 let count = read_required_control_word(
87 control_bytes,
88 control_offset_index(control::METRICS_BASE, i)?,
89 )?;
90 if count > 0 {
91 out.metrics.push((i, count));
92 }
93 }
94 out.tenant_fairness.clear();
95 reserve_target_capacity(
96 &mut out.tenant_fairness,
97 telemetry_u32_to_usize(control::TENANT_FAIRNESS_SLOTS, "tenant fairness slot count")?,
98 "tenant fairness",
99 )?;
100 for i in 0..control::TENANT_FAIRNESS_SLOTS {
101 out.tenant_fairness.push(read_required_control_word(
102 control_bytes,
103 control_offset_index(control::TENANT_FAIRNESS_BASE, i)?,
104 )?);
105 }
106 out.priority_fairness.clear();
107 reserve_target_capacity(
108 &mut out.priority_fairness,
109 telemetry_u32_to_usize(
110 control::PRIORITY_FAIRNESS_SLOTS,
111 "priority fairness slot count",
112 )?,
113 "priority fairness",
114 )?;
115 for i in 0..control::PRIORITY_FAIRNESS_SLOTS {
116 out.priority_fairness.push(read_required_control_word(
117 control_bytes,
118 control_offset_index(control::PRIORITY_FAIRNESS_BASE, i)?,
119 )?);
120 }
121 Ok(())
122 }
123}
124
125impl RingTelemetry {
126 #[must_use]
128 #[cfg(test)]
129 pub fn decode(control_bytes: &[u8], ring_bytes: &[u8]) -> Self {
130 Self::decode_with_window_opcodes(control_bytes, ring_bytes, &[])
131 }
132
133 pub fn try_decode(control_bytes: &[u8], ring_bytes: &[u8]) -> Result<Self, PipelineError> {
140 Self::try_decode_with_window_opcodes(control_bytes, ring_bytes, &[])
141 }
142
143 #[must_use]
147 #[cfg(test)]
148 pub fn decode_with_window_opcodes(
149 control_bytes: &[u8],
150 ring_bytes: &[u8],
151 window_opcodes: &[u32],
152 ) -> Self {
153 Self::try_decode_with_window_opcodes(control_bytes, ring_bytes, window_opcodes)
154 .unwrap_or_default()
155 }
156
157 #[cfg(test)]
160 pub fn decode_with_window_opcodes_into(
161 control_bytes: &[u8],
162 ring_bytes: &[u8],
163 window_opcodes: &[u32],
164 out: &mut Self,
165 scratch: &mut TelemetryDecodeScratch,
166 ) {
167 Self::try_decode_with_window_opcodes_into(
168 control_bytes,
169 ring_bytes,
170 window_opcodes,
171 out,
172 scratch,
173 )
174 .unwrap_or_else(|_| {
175 *out = Self::default();
176 scratch.clear();
177 });
178 }
179
180 fn try_decode_with_window_opcodes_into_unchecked(
181 control_bytes: &[u8],
182 ring_bytes: &[u8],
183 window_opcodes: &[u32],
184 out: &mut Self,
185 scratch: &mut TelemetryDecodeScratch,
186 ) -> Result<(), PipelineError> {
187 enum WindowOpcodeMatcher<'a> {
188 None,
189 Single(u32),
190 DenseBitmap(u128),
191 SmallSlice(&'a [u32]),
192 LargeSlice(&'a [u32]),
193 }
194
195 ControlSnapshot::try_decode_into(control_bytes, &mut out.control)?;
196 let slot_count = ring_bytes.len() / slot_byte_len()?;
197 out.occupancy = RingOccupancy::default();
198 out.slots.clear();
199 reserve_target_capacity(&mut out.slots, slot_count, "ring slots")?;
200 out.windows.clear();
201 scratch.window_opcodes.clear();
202 scratch.windows.clear();
203 let window_opcode_lookup = if window_opcodes.is_empty() {
204 &[][..]
205 } else if is_sorted_unique_u32(window_opcodes) {
206 window_opcodes
207 } else {
208 reserve_target_capacity(
209 &mut scratch.window_opcodes,
210 window_opcodes.len(),
211 "window opcode scratch",
212 )?;
213 scratch.window_opcodes.extend_from_slice(window_opcodes);
214 scratch.window_opcodes.sort_unstable();
215 scratch.window_opcodes.dedup();
216 scratch.window_opcodes.as_slice()
217 };
218 let window_opcode_matcher = match window_opcode_lookup {
219 [] => WindowOpcodeMatcher::None,
220 [opcode] => WindowOpcodeMatcher::Single(*opcode),
221 opcodes if opcodes.len() > 1 && opcodes.iter().all(|opcode| *opcode < 128) => {
222 let bitmap = opcodes
223 .iter()
224 .fold(0_u128, |acc, &opcode| acc | (1_u128 << opcode));
225 WindowOpcodeMatcher::DenseBitmap(bitmap)
226 }
227 opcodes if opcodes.len() <= 8 => WindowOpcodeMatcher::SmallSlice(opcodes),
228 opcodes => WindowOpcodeMatcher::LargeSlice(opcodes),
229 };
230 if !matches!(window_opcode_matcher, WindowOpcodeMatcher::None) {
231 reserve_hash_map_capacity(
232 &mut scratch.windows,
233 slot_count,
234 "window accumulator scratch",
235 )?;
236 }
237 let decode_windows = !matches!(window_opcode_matcher, WindowOpcodeMatcher::None);
238
239 let slot_byte_len = slot_byte_len()?;
240 for (slot_idx, slot_bytes) in ring_bytes.chunks_exact(slot_byte_len).enumerate() {
241 let slot_idx = u32::try_from(slot_idx).map_err(|source| {
242 PipelineError::Backend(format!(
243 "megakernel telemetry slot index cannot fit u32: {source}. Fix: shard ring snapshots before host decode."
244 ))
245 })?;
246 let status_raw = try_read_slot_chunk_word(slot_bytes, STATUS_WORD)?;
247 let status = RingStatus::from_raw(status_raw);
248 match status {
249 RingStatus::Empty => out.occupancy.empty += 1,
250 RingStatus::Published => out.occupancy.published += 1,
251 RingStatus::Claimed => out.occupancy.claimed += 1,
252 RingStatus::Done => out.occupancy.done += 1,
253 RingStatus::WaitIo => out.occupancy.wait_io += 1,
254 RingStatus::Yield => out.occupancy.yield_count += 1,
255 RingStatus::Requeue => out.occupancy.requeue += 1,
256 RingStatus::Fault => out.occupancy.fault += 1,
257 RingStatus::Unknown(_) => out.occupancy.unknown += 1,
258 }
259 let tenant_id = try_read_slot_chunk_word(slot_bytes, TENANT_WORD)?;
260 let opcode = try_read_slot_chunk_word(slot_bytes, OPCODE_WORD)?;
261 let args_prefix = [
262 try_read_slot_chunk_word(slot_bytes, ARG0_WORD)?,
263 try_read_slot_chunk_word(slot_bytes, ARG0_WORD + 1)?,
264 try_read_slot_chunk_word(slot_bytes, ARG0_WORD + 2)?,
265 ];
266 let is_window_opcode = match window_opcode_matcher {
267 WindowOpcodeMatcher::None => false,
268 WindowOpcodeMatcher::Single(expected) => opcode == expected,
269 WindowOpcodeMatcher::DenseBitmap(bitmap) => {
270 opcode < 128 && ((bitmap >> opcode) & 1) == 1
271 }
272 WindowOpcodeMatcher::SmallSlice(window_opcodes) => window_opcodes.contains(&opcode),
273 WindowOpcodeMatcher::LargeSlice(window_opcodes) => {
274 window_opcodes.binary_search(&opcode).is_ok()
275 }
276 };
277 if decode_windows && is_window_opcode {
278 let ticket = args_prefix[0];
279 let class_tag = args_prefix[1];
280 let entry =
281 scratch
282 .windows
283 .entry((ticket, opcode))
284 .or_insert_with(|| WindowAccumulator {
285 tenant_id,
286 opcode,
287 ..WindowAccumulator::default()
288 });
289 match class_tag {
290 0 => entry.required_slots += 1,
291 1 => entry.lookahead_slots += 1,
292 _ => {}
293 }
294 match status {
295 RingStatus::Published => entry.published += 1,
296 RingStatus::Claimed => entry.claimed += 1,
297 RingStatus::Done => entry.done += 1,
298 RingStatus::WaitIo => entry.wait_io += 1,
299 RingStatus::Yield => entry.yield_count += 1,
300 RingStatus::Requeue => entry.requeue += 1,
301 RingStatus::Fault => entry.fault += 1,
302 RingStatus::Empty | RingStatus::Unknown(_) => {}
303 }
304 }
305 out.slots.push(RingSlotSnapshot {
306 slot_idx,
307 status,
308 tenant_id,
309 opcode,
310 args_prefix,
311 });
312 }
313
314 reserve_target_capacity(&mut out.windows, scratch.windows.len(), "window output")?;
315 for (&(ticket, _), acc) in &scratch.windows {
316 out.windows.push(WindowTelemetry {
317 ticket,
318 tenant_id: acc.tenant_id,
319 opcode: acc.opcode,
320 required_slots: acc.required_slots,
321 lookahead_slots: acc.lookahead_slots,
322 published: acc.published,
323 claimed: acc.claimed,
324 done: acc.done,
325 wait_io: acc.wait_io,
326 yield_count: acc.yield_count,
327 requeue: acc.requeue,
328 fault: acc.fault,
329 });
330 }
331 out.windows
332 .sort_unstable_by_key(|window| (window.ticket, window.opcode));
333 Ok(())
334 }
335
336 pub fn try_decode_with_window_opcodes(
343 control_bytes: &[u8],
344 ring_bytes: &[u8],
345 window_opcodes: &[u32],
346 ) -> Result<Self, PipelineError> {
347 validate_telemetry_buffers(control_bytes, ring_bytes)?;
348 let mut out = Self::default();
349 let mut scratch = TelemetryDecodeScratch::new();
350 Self::try_decode_with_window_opcodes_into_unchecked(
351 control_bytes,
352 ring_bytes,
353 window_opcodes,
354 &mut out,
355 &mut scratch,
356 )?;
357 Ok(out)
358 }
359
360 pub fn try_decode_with_window_opcodes_into(
368 control_bytes: &[u8],
369 ring_bytes: &[u8],
370 window_opcodes: &[u32],
371 out: &mut Self,
372 scratch: &mut TelemetryDecodeScratch,
373 ) -> Result<(), PipelineError> {
374 validate_telemetry_buffers(control_bytes, ring_bytes)?;
375 Self::try_decode_with_window_opcodes_into_unchecked(
376 control_bytes,
377 ring_bytes,
378 window_opcodes,
379 out,
380 scratch,
381 )?;
382 Ok(())
383 }
384
385 #[must_use]
387 #[cfg(test)]
388 pub fn active_slots_for_opcode(&self, opcode: u32) -> Vec<&RingSlotSnapshot> {
389 self.try_active_slots_for_opcode(opcode).unwrap_or_default()
390 }
391
392 pub fn try_active_slots_for_opcode(
398 &self,
399 opcode: u32,
400 ) -> Result<Vec<&RingSlotSnapshot>, PipelineError> {
401 let mut out = Vec::default();
402 self.try_active_slots_for_opcode_into(opcode, &mut out)?;
403 Ok(out)
404 }
405
406 pub fn active_slots_for_opcode_iter(
408 &self,
409 opcode: u32,
410 ) -> impl Iterator<Item = &RingSlotSnapshot> {
411 self.slots
412 .iter()
413 .filter(move |slot| slot.opcode == opcode && slot.status.is_active())
414 }
415
416 pub fn try_active_slots_for_opcode_into<'a>(
422 &'a self,
423 opcode: u32,
424 out: &mut Vec<&'a RingSlotSnapshot>,
425 ) -> Result<(), PipelineError> {
426 out.clear();
427 reserve_target_capacity(out, self.slots.len(), "active slot output")?;
428 self.slots
429 .iter()
430 .filter(|slot| slot.opcode == opcode && slot.status.is_active())
431 .for_each(|slot| out.push(slot));
432 Ok(())
433 }
434
435 #[must_use]
437 #[cfg(test)]
438 pub fn active_windows(&self) -> Vec<&WindowTelemetry> {
439 self.try_active_windows().unwrap_or_default()
440 }
441
442 pub fn try_active_windows(&self) -> Result<Vec<&WindowTelemetry>, PipelineError> {
448 let mut out = Vec::default();
449 self.try_active_windows_into(&mut out)?;
450 Ok(out)
451 }
452
453 pub fn try_active_windows_into<'a>(
459 &'a self,
460 out: &mut Vec<&'a WindowTelemetry>,
461 ) -> Result<(), PipelineError> {
462 out.clear();
463 reserve_target_capacity(out, self.windows.len(), "active window output")?;
464 self.windows
465 .iter()
466 .filter(|window| window.is_active())
467 .for_each(|window| out.push(window));
468 Ok(())
469 }
470
471 #[must_use]
473 pub fn priority_accounting(&self) -> PriorityRequeueAccounting {
474 PriorityRequeueAccounting {
475 requeue_count: u64::from(self.occupancy.requeue),
476 aged_promotions: 0,
477 max_priority_age: 0,
478 }
479 }
480
481 #[must_use]
484 #[cfg(test)]
485 pub fn runtime_counters(&self) -> ResidentRuntimeCounters {
486 match self.try_runtime_counters() {
487 Ok(counters) => counters,
488 Err(_) => zero_runtime_counters(),
489 }
490 }
491
492 pub fn try_runtime_counters(&self) -> Result<ResidentRuntimeCounters, PipelineError> {
499 let total_slots = self.occupancy.total_slots();
500 let queue_depth = self.occupancy.queue_depth();
501 let gpu_idle_slots = self.occupancy.empty;
502 let gpu_idle_ppm = if total_slots == 0 {
503 0
504 } else {
505 let raw_idle_ppm = (u64::from(gpu_idle_slots) * 1_000_000) / u64::from(total_slots);
506 raw_idle_ppm.min(1_000_000) as u32
507 };
508 let frontier_density_bps = try_density_bps(queue_depth, total_slots)?;
509 let active_slots = total_slots.saturating_sub(gpu_idle_slots);
510 let occupancy_proxy_bps = try_density_bps(active_slots, total_slots)?;
511 let tenant_fairness_total = try_sum_u32_as_u64(
512 &self.control.tenant_fairness,
513 "tenant fairness total",
514 "shard tenant counters before telemetry aggregation",
515 )?;
516 let priority_fairness_total = try_sum_u32_as_u64(
517 &self.control.priority_fairness,
518 "priority fairness total",
519 "shard priority counters before telemetry aggregation",
520 )?;
521 let tenant_fairness_skew = try_fairness_skew(&self.control.tenant_fairness)?;
522 Ok(ResidentRuntimeCounters {
523 total_slots,
524 queue_depth,
525 gpu_idle_slots,
526 gpu_idle_ppm,
527 frontier_density_bps,
528 occupancy_proxy_bps,
529 drained_slots: self.control.done_count,
530 unreclaimed_done_slots: self.occupancy.done,
531 tenant_fairness_total,
532 tenant_fairness_skew,
533 priority_fairness_total,
534 requeue_slots: self.occupancy.requeue,
535 fault_slots: self.occupancy.fault,
536 })
537 }
538
539 #[must_use]
542 #[cfg(test)]
543 pub fn health_since(&self, previous: &RingTelemetry) -> ResidentWatchdogSnapshot {
544 match self.try_health_since(previous) {
545 Ok(snapshot) => snapshot,
546 Err(_) => zero_watchdog_snapshot(),
547 }
548 }
549
550 pub fn try_health_since(
557 &self,
558 previous: &RingTelemetry,
559 ) -> Result<ResidentWatchdogSnapshot, PipelineError> {
560 let counters = self.try_runtime_counters()?;
561 let done_delta = self
562 .control
563 .done_count
564 .checked_sub(previous.control.done_count)
565 .ok_or_else(|| {
566 errors::done_counter_backwards(previous.control.done_count, self.control.done_count)
567 })?;
568 let suspected_stall =
569 counters.queue_depth > 0 && done_delta == 0 && counters.fault_slots == 0;
570 Ok(ResidentWatchdogSnapshot {
571 done_delta,
572 queue_depth: counters.queue_depth,
573 fault_slots: counters.fault_slots,
574 requeue_slots: counters.requeue_slots,
575 gpu_idle_ppm: counters.gpu_idle_ppm,
576 suspected_stall,
577 })
578 }
579
580 pub fn recommend_launch(
586 &self,
587 mut request: ResidentLaunchRequest,
588 ) -> Result<ResidentLaunchRecommendation, vyre_driver::BackendError> {
589 let counters = self
590 .try_runtime_counters()
591 .map_err(errors::launch_telemetry_failed)?;
592 if request.graph_node_count == 0 {
593 request.graph_node_count = counters.total_slots;
594 }
595 if request.graph_edge_count == 0 {
596 request.graph_edge_count = counters.queue_depth;
597 }
598 if request.frontier_density_bps == 0 {
599 request.frontier_density_bps = counters.frontier_density_bps;
600 }
601 request.hot_opcode_count = self
602 .control
603 .metrics
604 .iter()
605 .filter(|(_, count)| *count > 0)
606 .count()
607 .try_into()
608 .map_err(errors::hot_opcode_count_overflow)?;
609 let mut hot_window_count = 0usize;
610 for window in &self.windows {
611 let demand = window
612 .required_slots
613 .checked_add(window.lookahead_slots)
614 .ok_or_else(|| errors::route_window_demand_overflow())?;
615 if demand >= 4 {
616 hot_window_count = hot_window_count
617 .checked_add(1)
618 .ok_or_else(|| errors::hot_window_count_overflow())?;
619 }
620 }
621 request.hot_window_count = hot_window_count
622 .try_into()
623 .map_err(errors::hot_window_count_too_wide)?;
624 request.requeue_count = request
625 .requeue_count
626 .checked_add(u64::from(self.occupancy.requeue))
627 .ok_or_else(errors::requeue_count_overflow)?;
628 ResidentLaunchPolicy::standard().recommend(request)
629 }
630}
631
632#[cfg(test)]
635fn zero_runtime_counters() -> ResidentRuntimeCounters {
636 ResidentRuntimeCounters {
637 total_slots: 0,
638 queue_depth: 0,
639 gpu_idle_slots: 0,
640 gpu_idle_ppm: 0,
641 frontier_density_bps: 0,
642 occupancy_proxy_bps: 0,
643 drained_slots: 0,
644 unreclaimed_done_slots: 0,
645 tenant_fairness_total: 0,
646 tenant_fairness_skew: 0,
647 priority_fairness_total: 0,
648 requeue_slots: 0,
649 fault_slots: 0,
650 }
651}
652
653#[cfg(test)]
656fn zero_watchdog_snapshot() -> ResidentWatchdogSnapshot {
657 ResidentWatchdogSnapshot {
658 done_delta: 0,
659 queue_depth: 0,
660 fault_slots: 0,
661 requeue_slots: 0,
662 gpu_idle_ppm: 0,
663 suspected_stall: false,
664 }
665}
666
667fn read_required_control_word(control_bytes: &[u8], word_idx: usize) -> Result<u32, PipelineError> {
668 read_word(control_bytes, word_idx).ok_or_else(|| errors::missing_control_word(word_idx))
669}
670
671fn try_density_bps(numerator: u32, denominator: u32) -> Result<u16, PipelineError> {
672 if denominator == 0 {
673 return Ok(0);
674 }
675 let bps = (u64::from(numerator) * 10_000) / u64::from(denominator);
676 u16::try_from(bps.min(u64::from(u16::MAX))).map_err(errors::density_bps_overflow)
677}
678
679fn validate_telemetry_buffers(
680 control_bytes: &[u8],
681 ring_bytes: &[u8],
682) -> Result<(), PipelineError> {
683 validate_control_snapshot(control_bytes)?;
684 let slot_bytes = slot_byte_len()?;
685 if ring_bytes.len() % slot_bytes != 0 {
686 return Err(errors::ring_slot_alignment(ring_bytes.len(), slot_bytes));
687 }
688 let slot_count = ring_bytes.len() / slot_bytes;
689 if u32::try_from(slot_count).is_err() {
690 return Err(errors::ring_slot_count_too_wide(slot_count));
691 }
692 Ok(())
693}
694
695fn validate_control_snapshot(control_bytes: &[u8]) -> Result<(), PipelineError> {
696 let min_control =
697 super::protocol::control_byte_len(0).ok_or_else(|| errors::control_length_overflow())?;
698 if control_bytes.len() < min_control || control_bytes.len() % 4 != 0 {
699 return Err(errors::bad_control_snapshot(
700 control_bytes.len(),
701 min_control,
702 ));
703 }
704 Ok(())
705}
706
707fn slot_byte_len() -> Result<usize, PipelineError> {
708 SLOT_WORDS_USIZE
709 .checked_mul(4)
710 .ok_or_else(|| errors::slot_byte_width_overflow())
711}
712
713fn telemetry_u32_to_usize(value: u32, label: &'static str) -> Result<usize, PipelineError> {
714 usize::try_from(value).map_err(|source| errors::telemetry_u32_to_usize(value, label, source))
715}
716
717fn control_word_index(word: u32) -> Result<usize, PipelineError> {
718 usize::try_from(word).map_err(|source| errors::control_word_index(word, source))
719}
720
721fn control_offset_index(base: u32, offset: u32) -> Result<usize, PipelineError> {
722 let word = base
723 .checked_add(offset)
724 .ok_or_else(|| errors::control_word_offset_overflow())?;
725 control_word_index(word)
726}
727
728fn try_sum_u32_as_u64(
729 counters: &[u32],
730 label: &'static str,
731 fix: &'static str,
732) -> Result<u64, PipelineError> {
733 counters.iter().try_fold(0u64, |acc, &count| {
734 acc.checked_add(u64::from(count))
735 .ok_or_else(|| errors::counter_sum_overflow(label, fix))
736 })
737}
738
739fn try_fairness_skew(counters: &[u32]) -> Result<u32, PipelineError> {
740 let mut min_nonzero = u32::MAX;
741 let mut max = 0u32;
742 for &count in counters {
743 if count != 0 {
744 min_nonzero = min_nonzero.min(count);
745 max = max.max(count);
746 }
747 }
748 if min_nonzero == u32::MAX {
749 Ok(0)
750 } else {
751 max.checked_sub(min_nonzero)
752 .ok_or_else(|| errors::fairness_skew_invalid(max, min_nonzero))
753 }
754}
755
756#[cfg(test)]
757#[path = "telemetry_tests.rs"]
758mod tests;