Files
kramer/src/mapping/map.rs
2025-12-31 17:22:57 -05:00

874 lines
23 KiB
Rust

use std::fs::File;
use std::io::Write;
use super::{Cluster, Domain, DomainOverlap, Stage};
use anyhow;
use ron::de::from_reader;
use ron::error::SpannedError;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct MapFile {
pub sector_size: usize,
pub domain: Domain,
pub map: Vec<Cluster>,
}
impl TryFrom<File> for MapFile {
type Error = SpannedError;
fn try_from(file: File) -> Result<Self, Self::Error> {
from_reader(file)
}
}
impl Default for MapFile {
fn default() -> Self {
MapFile {
sector_size: crate::FB_SECTOR_SIZE,
domain: Domain::default(),
map: vec![Cluster {
domain: Domain::default(),
stage: Stage::Untested,
}],
}
}
}
impl MapFile {
pub fn new(sector_size: usize) -> Self {
MapFile::default().set_sector_size(sector_size).to_owned()
}
pub fn set_sector_size(&mut self, sector_size: usize) -> &mut Self {
self.sector_size = sector_size;
self
}
/// Recalculate cluster mappings.
pub fn update(&mut self, new: Cluster) -> &mut Self {
let mut map: Vec<Cluster> = vec![Cluster::from(new.clone())];
for old in self.map.iter() {
let mut old = *old;
match new.domain.overlap(old.domain) {
DomainOverlap::None => map.push(old),
DomainOverlap::SelfEngulfsOther => (),
DomainOverlap::OtherEngulfsSelf => {
other_engulfs_self_update(new, &mut old, &mut map)
}
DomainOverlap::OtherOverlapsEnd => {
// Case 1
old.domain.start = new.domain.end;
map.push(old);
}
DomainOverlap::OtherOverlapsStart => {
// Case 2
old.domain.end = new.domain.start;
map.push(old);
}
};
}
self.map = map;
self
}
/// Get current recovery stage.
pub fn get_stage(&self) -> Stage {
let mut recover_stage = Stage::Damaged;
for cluster in self.map.iter() {
match cluster.stage {
Stage::Untested => return Stage::Untested,
Stage::ForIsolation { .. } => {
if recover_stage == Stage::Damaged || cluster.stage < recover_stage {
// Note that recover_stage after first condition is
// only ever Stage::ForIsolation(_), thus PartialEq,
// PartialOrd are useful for comparing the internal value.
recover_stage = cluster.stage
}
}
Stage::Damaged => (),
Stage::Intact => (),
}
}
recover_stage
}
/// Get clusters of common stage.
pub fn get_clusters(&self, stage: Stage) -> Vec<Cluster> {
self.map
.iter()
.filter_map(|mc| {
if mc.stage == stage {
Some(mc.to_owned())
} else {
None
}
})
.collect()
}
/// Defragments cluster groups.
/// I.E. check forwards every cluster from current until stage changes,
/// then group at once.
pub fn defrag(&mut self) -> &mut Self {
let mut new_map: Vec<Cluster> = vec![];
// Fetch first cluster.
let mut start_cluster = self.map.iter().find(|c| c.domain.start == 0).unwrap();
// Even though this would be initialized by its first read,
// the compiler won't stop whining, and idk how to assert that to it.
let mut end_cluster = Cluster::default();
let mut new_cluster: Cluster;
let mut stage_common: bool;
let mut is_finished = false;
while !is_finished {
stage_common = true;
// Start a new cluster based on the cluster following
// the end of last new_cluster.
new_cluster = start_cluster.to_owned();
// While stage is common, and not finished,
// find each trailing cluster.
while stage_common && !is_finished {
end_cluster = start_cluster.to_owned();
if end_cluster.domain.end != self.domain.end {
start_cluster = self
.map
.iter()
.find(|c| end_cluster.domain.end == c.domain.start)
.unwrap();
stage_common = new_cluster.stage == start_cluster.stage
} else {
is_finished = true;
}
}
// Set the new ending, encapsulating any clusters of common stage.
new_cluster.domain.end = end_cluster.domain.end;
new_map.push(new_cluster);
}
self.map = new_map;
self
}
/// Extend the domain of the MapFile.
/// Returns None if the domain cannot be changed or is unchanged.
/// Returns the delta of the previous domain end and the new end.
pub fn extend(&mut self, end: usize) -> Option<usize> {
if end <= self.domain.end {
return None;
}
let old_end = self.domain.end;
let delta = end - old_end;
self.domain.end = end;
// Add new data as untested.
self.update(Cluster {
domain: Domain {
start: old_end,
end: self.domain.end,
},
..Default::default()
});
Some(delta)
}
/// Writes the map to the provided item implementing `Write` trait.
/// Usually a file.
pub fn write_to<W: Write>(&mut self, file: &mut W) -> anyhow::Result<usize> {
self.defrag();
let written_bytes = file.write(
ron::ser::to_string_pretty(
self,
ron::ser::PrettyConfig::new()
.new_line("\n".to_string())
.struct_names(true),
)?
.as_bytes(),
)?;
Ok(written_bytes)
}
}
// This is split out for a shred of readability.
fn other_engulfs_self_update(new: Cluster, old: &mut Cluster, map: &mut Vec<Cluster>) {
if new.domain.start == old.domain.start {
// Case 6 of map::tests::test_update
old.domain.start = new.domain.end;
} else {
// Case 4 and part of 10
let old_end = old.domain.end;
old.domain.end = new.domain.start;
if new.domain.end != old_end {
// Case 10 of map::tests::test_update
map.push(Cluster {
domain: Domain {
start: new.domain.end,
end: old_end,
},
stage: old.stage,
})
}
}
map.push(old.to_owned())
}
#[cfg(test)]
mod tests {
use super::*;
/// Test for MapFile::update()
#[test]
fn update_1_new_overlaps_start() {
// Case 1:
// |----new----|
// |----old----|
//
// | --> |-old-|
// Solution: old.start = new.end
let mut map = MapFile {
map: vec![Cluster {
domain: Domain { start: 1, end: 3 },
..Default::default()
}],
..Default::default()
};
map.update(Cluster {
domain: Domain { start: 0, end: 2 },
..Default::default()
});
map.map.sort();
assert_eq!(
map.map,
vec![
Cluster {
domain: Domain { start: 0, end: 2 },
..Default::default()
},
Cluster {
domain: Domain { start: 2, end: 3 },
..Default::default()
}
]
);
}
/// Test for MapFile::update()
#[test]
fn update_2_new_overlaps_end() {
// Case 2:
// |----new----|
// |----old----|
//
// |-old-| <-- |
// Solution: old.end = new.start
let mut map = MapFile {
map: vec![Cluster {
domain: Domain { start: 0, end: 2 },
..Default::default()
}],
..Default::default()
};
map.update(Cluster {
domain: Domain { start: 1, end: 3 },
..Default::default()
});
map.map.sort();
assert_eq!(
map.map,
vec![
Cluster {
domain: Domain { start: 0, end: 1 },
..Default::default()
},
Cluster {
domain: Domain { start: 1, end: 3 },
..Default::default()
}
]
);
}
/// Test for MapFile::update()
#[test]
fn update_3_new_engulfs_common_end() {
// Case 3:
// |----new----|
// |--old--|
//
// Solution: Remove old.
let mut map = MapFile {
map: vec![Cluster {
domain: Domain { start: 1, end: 3 },
..Default::default()
}],
..Default::default()
};
map.update(Cluster {
domain: Domain { start: 0, end: 3 },
..Default::default()
});
map.map.sort();
assert_eq!(
map.map,
vec![Cluster {
domain: Domain { start: 0, end: 3 },
..Default::default()
}]
);
}
/// Test for MapFile::update()
#[test]
fn update_4_old_engulfs_common_end() {
// Case 4:
// |--new--|
// |-----old-----|
//
// |-old-| <---- |
// Solution: old.end = new.start
let mut map = MapFile {
map: vec![Cluster {
domain: Domain { start: 0, end: 3 },
..Default::default()
}],
..Default::default()
};
map.update(Cluster {
domain: Domain { start: 1, end: 3 },
..Default::default()
});
map.map.sort();
assert_eq!(
map.map,
vec![
Cluster {
domain: Domain { start: 0, end: 1 },
..Default::default()
},
Cluster {
domain: Domain { start: 1, end: 3 },
..Default::default()
}
]
);
}
/// Test for MapFile::update()
#[test]
fn update_5_new_engulfs_common_start() {
// Case 5:
// |-----new----|
// |--old--|
//
// Solution: Remove old.
let mut map = MapFile {
map: vec![Cluster {
domain: Domain { start: 0, end: 2 },
..Default::default()
}],
..Default::default()
};
map.update(Cluster {
domain: Domain { start: 0, end: 3 },
..Default::default()
});
map.map.sort();
assert_eq!(
map.map,
vec![Cluster {
domain: Domain { start: 0, end: 3 },
..Default::default()
}]
);
}
/// Test for MapFile::update()
#[test]
fn update_6_old_engulfs_common_start() {
// Case 6:
// |--new--|
// |-----old-----|
//
// | ----> |-old-|
// Solution: old.start = new.end
let mut map = MapFile {
map: vec![Cluster {
domain: Domain { start: 0, end: 3 },
..Default::default()
}],
..Default::default()
};
map.update(Cluster {
domain: Domain { start: 0, end: 2 },
..Default::default()
});
map.map.sort();
assert_eq!(
map.map,
vec![
Cluster {
domain: Domain { start: 0, end: 2 },
..Default::default()
},
Cluster {
domain: Domain { start: 2, end: 3 },
..Default::default()
}
]
);
}
/// Test for MapFile::update()
#[test]
fn update_7_new_precedes() {
// Case 7:
// |--new--|
// |--old--|
//
// Solution: Leave unchanged.
let mut map = MapFile {
map: vec![Cluster {
domain: Domain { start: 2, end: 3 },
..Default::default()
}],
..Default::default()
};
map.update(Cluster {
domain: Domain { start: 0, end: 2 },
..Default::default()
});
map.map.sort();
assert_eq!(
map.map,
vec![
Cluster {
domain: Domain { start: 0, end: 2 },
..Default::default()
},
Cluster {
domain: Domain { start: 2, end: 3 },
..Default::default()
}
]
);
}
/// Test for MapFile::update()
#[test]
fn update_8_new_trails() {
// Case 8:
// |--new--|
// |--old--|
// Solution: Leave unchanged.
let mut map = MapFile {
map: vec![Cluster {
domain: Domain { start: 0, end: 2 },
..Default::default()
}],
..Default::default()
};
map.update(Cluster {
domain: Domain { start: 2, end: 3 },
..Default::default()
});
map.map.sort();
assert_eq!(
map.map,
vec![
Cluster {
domain: Domain { start: 0, end: 2 },
..Default::default()
},
Cluster {
domain: Domain { start: 2, end: 3 },
..Default::default()
}
]
);
}
/// Test for MapFile::update()
#[test]
fn update_9_new_engulfs() {
// Case 9:
// |-----new-----|
// |--old--|
//
// Solution: Remove old.
let mut map = MapFile {
map: vec![Cluster {
domain: Domain { start: 1, end: 2 },
..Default::default()
}],
..Default::default()
};
map.update(Cluster {
domain: Domain { start: 0, end: 3 },
..Default::default()
});
map.map.sort();
assert_eq!(
map.map,
vec![Cluster {
domain: Domain { start: 0, end: 3 },
..Default::default()
}]
);
}
/// Test for MapFile::update()
#[test]
fn update_10_old_engulfs() {
// Case 10:
// |--new--|
// |--------------old--------------|
//
// |----old----| <---- |
// + |--fracture-|
// Solution: old.end = new.start
// && fracture:
// with fracture.start = new.end
// && fracture.end = old.original_end
let mut map = MapFile {
map: vec![Cluster {
domain: Domain { start: 0, end: 3 },
..Default::default()
}],
..Default::default()
};
map.update(Cluster {
domain: Domain { start: 1, end: 2 },
..Default::default()
});
map.map.sort();
assert_eq!(
map.map,
vec![
Cluster {
domain: Domain { start: 0, end: 1 },
..Default::default()
},
Cluster {
domain: Domain { start: 1, end: 2 },
..Default::default()
},
Cluster {
domain: Domain { start: 2, end: 3 },
..Default::default()
}
]
);
}
/// Test for MapFile::update()
#[test]
fn update_11_common_start_and_end() {
// Case 11:
// |--new--|
// |--old--|
//
// Solution: Remove old.
let mut map = MapFile {
map: vec![Cluster {
domain: Domain { start: 0, end: 3 },
stage: Stage::Untested,
}],
..Default::default()
};
map.update(Cluster {
domain: Domain { start: 0, end: 3 },
stage: Stage::Intact,
});
map.map.sort();
assert_eq!(
map.map,
vec![Cluster {
domain: Domain { start: 0, end: 3 },
stage: Stage::Intact
}]
);
}
/// Test for MapFile::update()
#[test]
fn update_12_new_out_of_range_preceding() {
// Case 12:
// |--new--|
// |--old--|
//
// Solution: Leave Unchanged.
let mut map = MapFile {
map: vec![Cluster {
domain: Domain { start: 2, end: 3 },
..Default::default()
}],
..Default::default()
};
map.update(Cluster {
domain: Domain { start: 0, end: 1 },
..Default::default()
});
map.map.sort();
assert_eq!(
map.map,
vec![
Cluster {
domain: Domain { start: 0, end: 1 },
..Default::default()
},
Cluster {
domain: Domain { start: 2, end: 3 },
..Default::default()
}
]
);
}
/// Test for MapFile::update()
#[test]
fn update_13_new_out_of_range_trailing() {
// Case 13:
// |--new--|
// |--old--|
//
// Solution: Leave Unchanged.
let mut map = MapFile {
map: vec![Cluster {
domain: Domain { start: 0, end: 1 },
..Default::default()
}],
..Default::default()
};
map.update(Cluster {
domain: Domain { start: 2, end: 3 },
..Default::default()
});
map.map.sort();
assert_eq!(
map.map,
vec![
Cluster {
domain: Domain { start: 0, end: 1 },
..Default::default()
},
Cluster {
domain: Domain { start: 2, end: 3 },
..Default::default()
}
]
);
}
/// Test for MapFile::get_stage()
#[test]
fn get_stage() {
let mut mf = MapFile::default();
let mut mf_stage = mf.get_stage();
// If this fails here, there's something SERIOUSLY wrong.
assert!(
mf_stage == Stage::Untested,
"Determined stage to be {:?}, when {:?} was expeccted.",
mf_stage,
Stage::Untested
);
let stages = vec![
Stage::Damaged,
Stage::ForIsolation { level: 1 },
Stage::ForIsolation { level: 0 },
Stage::Untested,
];
mf.map = vec![];
for stage in stages {
mf.map.push(*Cluster::default().set_stage(stage));
mf_stage = mf.get_stage();
assert!(
stage == mf_stage,
"Expected stage to be {:?}, determined {:?} instead.",
stage,
mf_stage
)
}
}
/// Test for MapFile::get_clusters()
#[test]
fn get_clusters() {
let mut mf = MapFile::default();
mf.map = vec![
*Cluster::default().set_stage(Stage::Damaged),
*Cluster::default().set_stage(Stage::ForIsolation { level: 0 }),
*Cluster::default().set_stage(Stage::ForIsolation { level: 1 }),
Cluster::default(),
Cluster::default(),
*Cluster::default().set_stage(Stage::ForIsolation { level: 1 }),
*Cluster::default().set_stage(Stage::ForIsolation { level: 0 }),
*Cluster::default().set_stage(Stage::Damaged),
];
let stages = vec![
Stage::Damaged,
Stage::ForIsolation { level: 1 },
Stage::ForIsolation { level: 0 },
Stage::Untested,
];
for stage in stages {
let expected = vec![
*Cluster::default().set_stage(stage),
*Cluster::default().set_stage(stage),
];
let received = mf.get_clusters(stage);
assert!(
expected == received,
"Expected clusters {:?}, got {:?}.",
expected,
received
)
}
}
/// Test for MapFile::defrag()
#[test]
fn defrag() {
let mut mf = MapFile {
sector_size: 1,
domain: Domain { start: 0, end: 8 },
map: vec![
Cluster {
domain: Domain { start: 0, end: 1 },
stage: Stage::Untested,
},
Cluster {
domain: Domain { start: 1, end: 2 },
stage: Stage::Untested,
},
Cluster {
domain: Domain { start: 2, end: 3 },
stage: Stage::Untested,
},
Cluster {
domain: Domain { start: 3, end: 4 },
stage: Stage::ForIsolation { level: 0 },
},
Cluster {
domain: Domain { start: 4, end: 5 },
stage: Stage::ForIsolation { level: 0 },
},
Cluster {
domain: Domain { start: 5, end: 6 },
stage: Stage::ForIsolation { level: 1 },
},
Cluster {
domain: Domain { start: 6, end: 7 },
stage: Stage::ForIsolation { level: 0 },
},
Cluster {
domain: Domain { start: 7, end: 8 },
stage: Stage::Damaged,
},
],
};
let expected = vec![
Cluster {
domain: Domain { start: 0, end: 3 },
stage: Stage::Untested,
},
Cluster {
domain: Domain { start: 3, end: 5 },
stage: Stage::ForIsolation { level: 0 },
},
Cluster {
domain: Domain { start: 5, end: 6 },
stage: Stage::ForIsolation { level: 1 },
},
Cluster {
domain: Domain { start: 6, end: 7 },
stage: Stage::ForIsolation { level: 0 },
},
Cluster {
domain: Domain { start: 7, end: 8 },
stage: Stage::Damaged,
},
];
mf.defrag();
let received = mf.map;
assert!(
expected == received,
"Expected {:?} after defragging, got {:?}.",
expected,
received
)
}
}