use super::types::Address;
#[derive(Debug, Clone)]
pub struct BadBlockInfo {
pub address: Address,
pub reason: BadBlockReason,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum BadBlockReason {
Factory,
Runtime,
EccFailure,
Manual,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum BadBlockStrategy {
#[default]
Fail,
Skip,
Include,
}
impl BadBlockStrategy {
pub fn should_continue(&self) -> bool {
matches!(self, Self::Skip | Self::Include)
}
pub fn should_include_bad(&self) -> bool {
matches!(self, Self::Include)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, serde::Serialize, serde::Deserialize)]
pub enum BlockStatus {
#[default]
Unknown,
Good,
BadFactory,
BadRuntime,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct BadBlockTable {
status: Vec<BlockStatus>,
}
impl BadBlockTable {
pub fn new(total_blocks: usize) -> Self {
Self {
status: vec![BlockStatus::Unknown; total_blocks],
}
}
pub fn len(&self) -> usize {
self.status.len()
}
pub fn is_empty(&self) -> bool {
self.status.is_empty()
}
pub fn set_status(&mut self, block: usize, status: BlockStatus) {
if block < self.status.len() {
self.status[block] = status;
}
}
pub fn get_status(&self, block: usize) -> BlockStatus {
if block < self.status.len() {
self.status[block]
} else {
BlockStatus::Unknown
}
}
pub fn is_bad(&self, block: usize) -> bool {
matches!(
self.get_status(block),
BlockStatus::BadFactory | BlockStatus::BadRuntime
)
}
pub fn bad_block_count(&self) -> usize {
self.status
.iter()
.filter(|&&s| s == BlockStatus::BadFactory || s == BlockStatus::BadRuntime)
.count()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_bad_block_strategy_logic() {
let fail = BadBlockStrategy::Fail;
assert!(!fail.should_continue());
assert!(!fail.should_include_bad());
let skip = BadBlockStrategy::Skip;
assert!(skip.should_continue());
assert!(!skip.should_include_bad());
let include = BadBlockStrategy::Include;
assert!(include.should_continue());
assert!(include.should_include_bad());
}
#[test]
fn test_bad_block_table_operations() {
let mut bbt = BadBlockTable::new(10);
assert_eq!(bbt.len(), 10);
assert!(!bbt.is_empty());
assert_eq!(bbt.get_status(0), BlockStatus::Unknown);
assert!(!bbt.is_bad(0));
bbt.set_status(1, BlockStatus::BadFactory);
bbt.set_status(5, BlockStatus::BadRuntime);
bbt.set_status(8, BlockStatus::Good);
assert_eq!(bbt.get_status(1), BlockStatus::BadFactory);
assert!(bbt.is_bad(1));
assert_eq!(bbt.get_status(5), BlockStatus::BadRuntime);
assert!(bbt.is_bad(5));
assert_eq!(bbt.get_status(8), BlockStatus::Good);
assert!(!bbt.is_bad(8));
assert_eq!(bbt.get_status(100), BlockStatus::Unknown);
assert_eq!(bbt.bad_block_count(), 2);
}
}