use alloc::string::String;
use alloc::vec::Vec;
use core::fmt;
#[cfg(target_has_atomic = "32")]
use resident_fat::counted::{Counters, Counts};
use crate::apply::Progress;
use crate::bundle::{Entry, Role};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Phase {
pub ms: u64,
pub bytes: usize,
#[cfg(target_has_atomic = "32")]
pub counts: Option<Counts>,
}
impl Phase {
pub fn rate_kib_s(&self) -> u64 {
rate_kib_s(self.bytes, self.ms)
}
}
pub fn rate_kib_s(bytes: usize, ms: u64) -> u64 {
if ms == 0 {
return 0;
}
(bytes as u64).saturating_mul(1000) / ms.saturating_mul(1024)
}
impl fmt::Display for Phase {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} ms ({} KiB/s)", self.ms, self.rate_kib_s())?;
#[cfg(target_has_atomic = "32")]
if let Some(counts) = &self.counts {
write!(f, ", {counts}")?;
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Outcome {
Written {
write: Phase,
verify: Phase,
},
Unchanged {
read: Phase,
},
Unfinished {
write: Option<Phase>,
},
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Timed {
pub path: String,
pub role: Role,
pub bytes: usize,
pub outcome: Outcome,
}
impl fmt::Display for Timed {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "/{} ({} bytes): ", self.path, self.bytes)?;
match &self.outcome {
Outcome::Written { write, verify } => write!(f, "write {write}, verify {verify}"),
Outcome::Unchanged { read } => write!(f, "unchanged, not rewritten -- read in {read}"),
Outcome::Unfinished { write: Some(write) } => {
write!(f, "write {write}, read-back did not finish")
}
Outcome::Unfinished { write: None } => f.write_str("write did not finish"),
}
}
}
pub struct Measure<'c, F> {
now_ms: F,
#[cfg(target_has_atomic = "32")]
counters: Option<&'c Counters>,
#[cfg(not(target_has_atomic = "32"))]
counters: core::marker::PhantomData<&'c ()>,
started_ms: u64,
phase_ms: u64,
#[cfg(target_has_atomic = "32")]
started_counts: Counts,
#[cfg(target_has_atomic = "32")]
phase_counts: Counts,
entries: Vec<Timed>,
}
impl<'c, F: FnMut() -> u64> Measure<'c, F> {
pub fn new(mut now_ms: F) -> Self {
let started_ms = now_ms();
Measure {
now_ms,
#[cfg(target_has_atomic = "32")]
counters: None,
#[cfg(not(target_has_atomic = "32"))]
counters: core::marker::PhantomData,
started_ms,
phase_ms: started_ms,
#[cfg(target_has_atomic = "32")]
started_counts: Counts::default(),
#[cfg(target_has_atomic = "32")]
phase_counts: Counts::default(),
entries: Vec::new(),
}
}
#[cfg(target_has_atomic = "32")]
pub fn counting(now_ms: F, counters: &'c Counters) -> Self {
let mut measure = Measure::new(now_ms);
measure.counters = Some(counters);
measure.started_counts = counters.now();
measure.phase_counts = measure.started_counts;
measure
}
pub fn entries(&self) -> &[Timed] {
&self.entries
}
pub fn kernel(&self) -> Option<(Phase, Phase)> {
self.entries.iter().find_map(|entry| match entry.outcome {
Outcome::Written { write, verify } if entry.role == Role::Kernel => {
Some((write, verify))
}
_ => None,
})
}
pub fn elapsed_ms(&mut self) -> u64 {
(self.now_ms)().saturating_sub(self.started_ms)
}
#[cfg(target_has_atomic = "32")]
pub fn counts(&self) -> Option<Counts> {
self.counters
.map(|counters| counters.since(self.started_counts))
}
fn begin(&mut self) {
self.phase_ms = (self.now_ms)();
#[cfg(target_has_atomic = "32")]
if let Some(counters) = self.counters {
self.phase_counts = counters.now();
}
}
fn end(&mut self, bytes: usize) -> Phase {
let now = (self.now_ms)();
let phase = Phase {
ms: now.saturating_sub(self.phase_ms),
bytes,
#[cfg(target_has_atomic = "32")]
counts: self
.counters
.map(|counters| counters.since(self.phase_counts)),
};
self.begin();
phase
}
fn current(&mut self) -> Option<&mut Timed> {
self.entries.last_mut()
}
}
impl<F: FnMut() -> u64> Progress for Measure<'_, F> {
fn starting(&mut self, entry: Entry<'_>) {
self.entries.push(Timed {
path: String::from(entry.path),
role: entry.role,
bytes: entry.data.len(),
outcome: Outcome::Unfinished { write: None },
});
self.begin();
}
fn wrote(&mut self, entry: Entry<'_>) {
let write = self.end(entry.data.len());
if let Some(current) = self.current() {
current.outcome = Outcome::Unfinished { write: Some(write) };
}
}
fn verified(&mut self, entry: Entry<'_>) {
let verify = self.end(entry.data.len());
if let Some(current) = self.current() {
if let Outcome::Unfinished { write: Some(write) } = current.outcome {
current.outcome = Outcome::Written { write, verify };
}
}
}
fn skipped(&mut self, entry: Entry<'_>) {
let read = self.end(entry.data.len());
if let Some(current) = self.current() {
current.outcome = Outcome::Unchanged { read };
}
}
}