v0.13.24 — MapStats: split bytes_pending into nontried / retryable
bytes_pending was an opaque aggregate of NonTried + NonTrimmed + NonScraped. UIs that wanted a "will retry in Pass 2-N" bucket were stuck showing the entire unread disc as Maybe at pct=0. Adds two granular fields to MapStats: bytes_nontried — Pass 1 hasn't read these yet bytes_retryable — NonTrimmed + NonScraped, Pass 2-N will retry bytes_pending stays for back-compat (= bytes_nontried + bytes_retryable). Also picks up the cargo fmt --check lint that's been red on main CI since v0.13.18 (rustfmt fold differences on a few long format-string layouts; functional no-op).
This commit is contained in:
+23
-2
@@ -69,12 +69,28 @@ pub struct MapEntry {
|
||||
}
|
||||
|
||||
/// Summary statistics over all entries.
|
||||
///
|
||||
/// `bytes_pending` aggregates `NonTried + NonTrimmed + NonScraped` for
|
||||
/// back-compat. `bytes_nontried` and `bytes_retryable` (= NonTrimmed +
|
||||
/// NonScraped) split that aggregate so UIs can distinguish *unread*
|
||||
/// territory (still ahead of Pass 1's read head) from *needs-retry*
|
||||
/// territory (Pass 1 already encountered, queued for Pass 2-N).
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct MapStats {
|
||||
pub bytes_total: u64,
|
||||
pub bytes_good: u64,
|
||||
pub bytes_unreadable: u64,
|
||||
pub bytes_pending: u64,
|
||||
/// Sectors Pass 1 hasn't reached yet (`NonTried`). Subset of
|
||||
/// `bytes_pending`.
|
||||
pub bytes_nontried: u64,
|
||||
/// Sectors flagged for Pass 2-N retry — `NonTrimmed` (multi-sector
|
||||
/// read failed; needs split) + `NonScraped` (small-block read
|
||||
/// partially recovered; remainder still pending). Subset of
|
||||
/// `bytes_pending`. This is the right signal for a "MAYBE / will
|
||||
/// retry" UI bucket; `bytes_pending` over-counts because it folds
|
||||
/// in `bytes_nontried`.
|
||||
pub bytes_retryable: u64,
|
||||
}
|
||||
|
||||
/// Write-through mapfile. Every `record()` persists to disk immediately
|
||||
@@ -275,8 +291,13 @@ impl Mapfile {
|
||||
match e.status {
|
||||
SectorStatus::Finished => s.bytes_good += e.size,
|
||||
SectorStatus::Unreadable => s.bytes_unreadable += e.size,
|
||||
SectorStatus::NonTried | SectorStatus::NonTrimmed | SectorStatus::NonScraped => {
|
||||
s.bytes_pending += e.size
|
||||
SectorStatus::NonTried => {
|
||||
s.bytes_pending += e.size;
|
||||
s.bytes_nontried += e.size;
|
||||
}
|
||||
SectorStatus::NonTrimmed | SectorStatus::NonScraped => {
|
||||
s.bytes_pending += e.size;
|
||||
s.bytes_retryable += e.size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-10
@@ -1393,11 +1393,7 @@ impl Disc {
|
||||
// Fast path — full block read cleanly.
|
||||
read_ok_count += 1;
|
||||
if opts.decrypt {
|
||||
crate::decrypt::decrypt_sectors(
|
||||
&mut buf[..block_bytes_usz],
|
||||
&keys,
|
||||
0,
|
||||
)?;
|
||||
crate::decrypt::decrypt_sectors(&mut buf[..block_bytes_usz], &keys, 0)?;
|
||||
}
|
||||
file.seek(SeekFrom::Start(pos))
|
||||
.map_err(|e| Error::IoError { source: e })?;
|
||||
@@ -1496,11 +1492,7 @@ impl Disc {
|
||||
read_ok_count += 1;
|
||||
consecutive_good = consecutive_good.saturating_add(1);
|
||||
if opts.decrypt {
|
||||
crate::decrypt::decrypt_sectors(
|
||||
&mut buf[..one_bytes],
|
||||
&keys,
|
||||
0,
|
||||
)?;
|
||||
crate::decrypt::decrypt_sectors(&mut buf[..one_bytes], &keys, 0)?;
|
||||
}
|
||||
file.seek(SeekFrom::Start(s_pos))
|
||||
.map_err(|e| Error::IoError { source: e })?;
|
||||
|
||||
+4
-1
@@ -241,7 +241,10 @@ impl Drive {
|
||||
5_000,
|
||||
) {
|
||||
Ok(_) => DriveStatus::DiscPresent,
|
||||
Err(ref e) if e.scsi_sense().is_some_and(|s| s.is_not_ready() || s.is_unit_attention()) => {
|
||||
Err(ref e)
|
||||
if e.scsi_sense()
|
||||
.is_some_and(|s| s.is_not_ready() || s.is_unit_attention()) =>
|
||||
{
|
||||
DriveStatus::NotReady
|
||||
}
|
||||
_ => DriveStatus::Unknown,
|
||||
|
||||
+2
-10
@@ -389,13 +389,7 @@ impl std::fmt::Display for Error {
|
||||
s.asc,
|
||||
s.ascq,
|
||||
),
|
||||
None => write!(
|
||||
f,
|
||||
"E{}: 0x{:02x}/0x{:02x}",
|
||||
self.code(),
|
||||
opcode,
|
||||
status,
|
||||
),
|
||||
None => write!(f, "E{}: 0x{:02x}/0x{:02x}", self.code(), opcode, status,),
|
||||
},
|
||||
Error::IoError { source } => write!(f, "E{}: {}", self.code(), source),
|
||||
Error::DiscRead { sector } => write!(f, "E{}: {}", self.code(), sector),
|
||||
@@ -469,9 +463,7 @@ impl Error {
|
||||
/// no sense data exists).
|
||||
pub fn scsi_sense(&self) -> Option<&crate::scsi::ScsiSense> {
|
||||
match self {
|
||||
Error::ScsiError {
|
||||
sense: Some(s), ..
|
||||
} => Some(s),
|
||||
Error::ScsiError { sense: Some(s), .. } => Some(s),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
+4
-4
@@ -134,10 +134,10 @@ pub use decrypt::{DecryptKeys, decrypt_sectors};
|
||||
// background. The codec / channel / resolution enums are the canonical
|
||||
// structured representation; never compare against display strings.
|
||||
pub use disc::{
|
||||
AacsState, AudioChannels, AudioStream, Clip, Codec, ColorSpace, ContentFormat,
|
||||
DamageSeverity, Disc, DiscFormat, DiscId, DiscTitle, Extent, FrameRate, HdrFormat,
|
||||
KeySource, LabelPurpose, LabelQualifier, Resolution, SampleRate, ScanOptions, Stream,
|
||||
SubtitleStream, VideoStream, classify_damage,
|
||||
AacsState, AudioChannels, AudioStream, Clip, Codec, ColorSpace, ContentFormat, DamageSeverity,
|
||||
Disc, DiscFormat, DiscId, DiscTitle, Extent, FrameRate, HdrFormat, KeySource, LabelPurpose,
|
||||
LabelQualifier, Resolution, SampleRate, ScanOptions, Stream, SubtitleStream, VideoStream,
|
||||
classify_damage,
|
||||
};
|
||||
|
||||
// ─── Streams ────────────────────────────────────────────────────────────────
|
||||
|
||||
+5
-1
@@ -620,7 +620,11 @@ mod parse_sense_tests {
|
||||
// independently of the response code. parse_sense_key must mask
|
||||
// it off before classifying the format.
|
||||
let s = buf(0xF2, 0x05, 0x77);
|
||||
assert_eq!(parse_sense_key(&s, 8), 5, "VALID-bit must not leak into format detection");
|
||||
assert_eq!(
|
||||
parse_sense_key(&s, 8),
|
||||
5,
|
||||
"VALID-bit must not leak into format detection"
|
||||
);
|
||||
let s = buf(0xF0, 0x77, 0x02);
|
||||
assert_eq!(parse_sense_key(&s, 18), 2);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user