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, } impl TryFrom for MapFile { type Error = SpannedError; fn try_from(file: File) -> Result { 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 = 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 { 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 = 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 { 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(&mut self, file: &mut W) -> anyhow::Result { 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) { 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 ) } }