269 lines
7.3 KiB
Rust
269 lines
7.3 KiB
Rust
use ron::de::{from_reader, SpannedError};
|
|
use serde::Deserialize;
|
|
use std::fs::File;
|
|
|
|
use crate::FB_SECTOR_SIZE;
|
|
|
|
|
|
/// Domain, in sectors.
|
|
/// Requires sector_size to be provided elsewhere for conversion to bytes.
|
|
#[derive(Clone, Copy, Debug, Deserialize)]
|
|
pub struct Domain {
|
|
pub start: usize,
|
|
pub end: usize,
|
|
}
|
|
|
|
impl Default for Domain {
|
|
fn default() -> Self {
|
|
Domain { start: 0, end: 1 }
|
|
}
|
|
}
|
|
|
|
impl Domain {
|
|
/// Return length of domain in sectors.
|
|
pub fn len(self) -> usize {
|
|
self.end - self.start
|
|
}
|
|
}
|
|
|
|
/// A map for data stored in memory for processing and saving to disk.
|
|
#[allow(unused)]
|
|
#[derive(Clone, Debug, Deserialize)]
|
|
pub struct Cluster {
|
|
data: Option<Vec<u8>>,
|
|
domain: Domain,
|
|
status: Status,
|
|
}
|
|
|
|
impl Default for Cluster {
|
|
fn default() -> Self {
|
|
Cluster {
|
|
data: None,
|
|
domain: Domain::default(),
|
|
status: Status::default()
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/// Map for data stored on disk.
|
|
/// Rather have a second cluster type than inflating size
|
|
/// of output map by defining Option::None constantly.
|
|
#[derive(Clone, Copy, Debug, Deserialize)]
|
|
pub struct MapCluster {
|
|
pub domain: Domain,
|
|
pub status: Status,
|
|
}
|
|
|
|
impl Default for MapCluster {
|
|
fn default() -> Self {
|
|
MapCluster { domain: Domain::default(), status: Status::default() }
|
|
}
|
|
}
|
|
|
|
impl From<Cluster> for MapCluster {
|
|
fn from(cluster: Cluster) -> Self {
|
|
MapCluster {
|
|
domain: cluster.domain,
|
|
status: cluster.status,
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, PartialOrd)]
|
|
pub enum Status {
|
|
Untested,
|
|
ForIsolation(u8),
|
|
Damaged,
|
|
}
|
|
|
|
impl Default for Status {
|
|
fn default() -> Self {
|
|
Status::Untested
|
|
}
|
|
}
|
|
|
|
|
|
#[allow(unused)]
|
|
#[derive(Clone, Debug, Deserialize)]
|
|
pub struct MapFile {
|
|
pub sector_size: u16,
|
|
pub domain: Domain,
|
|
pub map: Vec<MapCluster>,
|
|
}
|
|
|
|
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: FB_SECTOR_SIZE,
|
|
domain: Domain::default(),
|
|
map: vec![MapCluster {
|
|
domain: Domain::default(),
|
|
status: Status::Untested,
|
|
}],
|
|
}
|
|
}
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
impl MapFile {
|
|
pub fn new(sector_size: u16) -> Self {
|
|
MapFile::default()
|
|
.set_sector_size(sector_size)
|
|
.to_owned()
|
|
}
|
|
|
|
pub fn set_sector_size(&mut self, sector_size: u16) -> &mut Self {
|
|
self.sector_size = sector_size;
|
|
self
|
|
}
|
|
|
|
/// Recalculate cluster mappings.
|
|
fn update(self, new_cluster: Cluster) {
|
|
let mut new_map: Vec<MapCluster> = vec![MapCluster::from(new_cluster.to_owned())];
|
|
|
|
for map_cluster in self.map.iter() {
|
|
let mut map_cluster = *map_cluster;
|
|
|
|
// If new_cluster doesn't start ahead and end short, map_cluster is forgotten.
|
|
if new_cluster.domain.start < map_cluster.domain.start
|
|
&& new_cluster.domain.end < map_cluster.domain.end {
|
|
/*
|
|
new_cluster overlaps the start of map_cluster,
|
|
but ends short of map_cluster end.
|
|
|
|
ACTION: Crop map_cluster to start at end of new_cluster.
|
|
*/
|
|
|
|
map_cluster.domain.start = new_cluster.domain.end;
|
|
new_map.push(map_cluster);
|
|
|
|
} else if new_cluster.domain.end < map_cluster.domain.end {
|
|
/*
|
|
new_cluster starts within map_cluster domain.
|
|
|
|
ACTION: Crop
|
|
*/
|
|
|
|
let domain_end = map_cluster.domain.end;
|
|
|
|
// Crop current object.
|
|
map_cluster.domain.end = new_cluster.domain.start;
|
|
new_map.push(map_cluster);
|
|
|
|
if new_cluster.domain.end < map_cluster.domain.end {
|
|
/*
|
|
new_cluster is within map_cluster.
|
|
|
|
ACTION: Crop & Fracture map_cluster
|
|
NOTE: Crop completed above.
|
|
*/
|
|
|
|
new_map.push(MapCluster {
|
|
domain: Domain {
|
|
start: new_cluster.domain.end,
|
|
end: domain_end,
|
|
},
|
|
status: map_cluster.status.to_owned()
|
|
});
|
|
}
|
|
} else {
|
|
/*
|
|
No overlap.
|
|
|
|
ACTION: Transfer
|
|
*/
|
|
|
|
new_map.push(map_cluster);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Get current recovery status.
|
|
pub fn get_state(self) -> Status {
|
|
let mut recover_status = Status::Damaged;
|
|
|
|
for cluster in self.map {
|
|
match cluster.status {
|
|
Status::Untested => return Status::Untested,
|
|
Status::ForIsolation(_) => {
|
|
if recover_status == Status::Damaged
|
|
|| cluster.status < recover_status {
|
|
// Note that recover_status after first condition is
|
|
// only ever Status::ForIsolation(_), thus PartialEq,
|
|
// PartialOrd are useful for comparing the internal value.
|
|
recover_status = cluster.status
|
|
}
|
|
},
|
|
Status::Damaged => (),
|
|
}
|
|
}
|
|
|
|
recover_status
|
|
}
|
|
|
|
/// Get clusters of common status.
|
|
pub fn get_clusters(self, state: Status) -> Vec<MapCluster> {
|
|
self.map.iter()
|
|
.filter_map(|mc| {
|
|
if mc.status == state { Some(mc.to_owned()) } else { None }
|
|
})
|
|
.collect()
|
|
}
|
|
|
|
/// Defragments cluster groups.
|
|
/// I.E. check forwards every cluster from current until status changes,
|
|
/// then group at once.
|
|
fn defrag(&mut self) {
|
|
let mut new_map: Vec<MapCluster> = vec![];
|
|
|
|
let mut pos: usize = 0;
|
|
|
|
// Fetch first cluster.
|
|
let mut start_cluster = *self.map.iter()
|
|
.find(|c| c.domain.start == pos)
|
|
.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 = MapCluster::default();
|
|
let mut new_cluster: MapCluster;
|
|
|
|
let mut status_common: bool;
|
|
|
|
while pos < self.domain.end {
|
|
status_common = true;
|
|
|
|
// Start a new cluster based on the cluster following
|
|
// the end of last new_cluster.
|
|
new_cluster = start_cluster;
|
|
|
|
// While status is common, find each trailing cluster.
|
|
while status_common {
|
|
// start_cluster was of common status to end_cluster.
|
|
end_cluster = start_cluster;
|
|
|
|
start_cluster = *self.map.iter()
|
|
.find(|c| end_cluster.domain.end == c.domain.start)
|
|
.unwrap();
|
|
|
|
status_common = new_cluster.status == start_cluster.status
|
|
}
|
|
|
|
// Set the new ending, encapsulating any clusters of common status.
|
|
new_cluster.domain.end = end_cluster.domain.end;
|
|
pos = new_cluster.domain.end;
|
|
new_map.push(new_cluster);
|
|
}
|
|
|
|
self.map = new_map;
|
|
}
|
|
} |