use crate::inode::Inode;
use crate::iters::file_blocks::FsBlockIndex;
use crate::util::read_u32le;
use crate::{Ext4, Ext4Error};
use alloc::vec;
use alloc::vec::Vec;
pub(super) struct BlockMap {
fs: Ext4,
level_0: [u32; 15],
level_0_index: usize,
num_blocks_yielded: u32,
num_blocks_total: u32,
level_1: Option<IndirectBlockIter>,
level_2: Option<DoubleIndirectBlockIter>,
level_3: Option<TripleIndirectBlockIter>,
is_done: bool,
}
impl BlockMap {
const NUM_ENTRIES: usize = 15;
pub(super) fn new(fs: Ext4, inode: &Inode) -> Self {
let mut level_0 = [0; Self::NUM_ENTRIES];
for (i, dst) in level_0.iter_mut().enumerate() {
let src_offset: usize = i.checked_mul(size_of::<u32>()).unwrap();
*dst = read_u32le(&inode.inline_data, src_offset);
}
Self {
fs,
level_0,
num_blocks_yielded: 0,
num_blocks_total: inode.file_size_in_blocks(),
level_0_index: 0,
level_1: None,
level_2: None,
level_3: None,
is_done: false,
}
}
#[track_caller]
fn increment_num_blocks_yielded(&mut self) {
self.num_blocks_yielded =
self.num_blocks_yielded.checked_add(1).unwrap();
}
fn next_impl(&mut self) -> Result<Option<FsBlockIndex>, Ext4Error> {
if self.num_blocks_yielded >= self.num_blocks_total {
self.is_done = true;
return Ok(None);
}
let Some(block_0) = self.level_0.get(self.level_0_index).copied()
else {
self.is_done = true;
return Ok(None);
};
let ret: u32 = if self.level_0_index <= 11 {
self.level_0_index = self.level_0_index.checked_add(1).unwrap();
self.increment_num_blocks_yielded();
block_0
} else if self.level_0_index == 12 {
if let Some(level_1) = &mut self.level_1 {
if let Some(block_index) = level_1.next() {
self.increment_num_blocks_yielded();
return Ok(Some(FsBlockIndex::from(block_index)));
} else {
self.level_1 = None;
self.level_0_index = 13;
return Ok(None);
}
} else {
self.level_1 =
Some(IndirectBlockIter::new(self.fs.clone(), block_0)?);
return Ok(None);
}
} else if self.level_0_index == 13 {
if let Some(level_2) = &mut self.level_2 {
if let Some(block_index) = level_2.next() {
let block_index = block_index?;
self.increment_num_blocks_yielded();
return Ok(Some(FsBlockIndex::from(block_index)));
} else {
self.level_2 = None;
self.level_0_index = 14;
return Ok(None);
}
} else {
self.level_2 = Some(DoubleIndirectBlockIter::new(
self.fs.clone(),
block_0,
)?);
return Ok(None);
}
} else if self.level_0_index == 14 {
if let Some(level_3) = &mut self.level_3 {
if let Some(block_index) = level_3.next() {
let block_index = block_index?;
self.increment_num_blocks_yielded();
return Ok(Some(FsBlockIndex::from(block_index)));
} else {
self.level_3 = None;
self.level_0_index = 15;
return Ok(None);
}
} else {
self.level_3 = Some(TripleIndirectBlockIter::new(
self.fs.clone(),
block_0,
)?);
return Ok(None);
}
} else {
todo!();
};
Ok(Some(FsBlockIndex::from(ret)))
}
}
impl_result_iter!(BlockMap, FsBlockIndex);
struct IndirectBlockIter {
block: Vec<u8>,
index_within_block: usize,
}
impl IndirectBlockIter {
fn new(fs: Ext4, block_index: u32) -> Result<Self, Ext4Error> {
let mut block = vec![0u8; fs.0.superblock.block_size.to_usize()];
fs.read_from_block(FsBlockIndex::from(block_index), 0, &mut block)?;
Ok(Self {
block,
index_within_block: 0,
})
}
}
impl Iterator for IndirectBlockIter {
type Item = u32;
fn next(&mut self) -> Option<u32> {
if self.index_within_block >= self.block.len() {
return None;
}
let block_index = read_u32le(&self.block, self.index_within_block);
self.index_within_block = self
.index_within_block
.checked_add(size_of::<u32>())
.unwrap();
Some(block_index)
}
}
struct DoubleIndirectBlockIter {
fs: Ext4,
indirect_0: IndirectBlockIter,
indirect_1: Option<IndirectBlockIter>,
is_done: bool,
}
impl DoubleIndirectBlockIter {
fn new(fs: Ext4, block_index: u32) -> Result<Self, Ext4Error> {
Ok(Self {
indirect_0: IndirectBlockIter::new(fs.clone(), block_index)?,
indirect_1: None,
fs,
is_done: false,
})
}
fn next_impl(&mut self) -> Result<Option<u32>, Ext4Error> {
if let Some(indirect_1) = &mut self.indirect_1 {
if let Some(block_index) = indirect_1.next() {
Ok(Some(block_index))
} else {
self.indirect_1 = None;
Ok(None)
}
} else if let Some(block_index) = self.indirect_0.next() {
self.indirect_1 =
Some(IndirectBlockIter::new(self.fs.clone(), block_index)?);
Ok(None)
} else {
self.is_done = true;
Ok(None)
}
}
}
impl_result_iter!(DoubleIndirectBlockIter, u32);
struct TripleIndirectBlockIter {
fs: Ext4,
indirect_0: IndirectBlockIter,
indirect_1: Option<DoubleIndirectBlockIter>,
is_done: bool,
}
impl TripleIndirectBlockIter {
fn new(fs: Ext4, block_index: u32) -> Result<Self, Ext4Error> {
Ok(Self {
indirect_0: IndirectBlockIter::new(fs.clone(), block_index)?,
indirect_1: None,
fs,
is_done: false,
})
}
fn next_impl(&mut self) -> Result<Option<u32>, Ext4Error> {
if let Some(indirect_1) = &mut self.indirect_1 {
if let Some(block_index) = indirect_1.next() {
let block_index = block_index?;
Ok(Some(block_index))
} else {
self.indirect_1 = None;
Ok(None)
}
} else if let Some(block_index) = self.indirect_0.next() {
self.indirect_1 = Some(DoubleIndirectBlockIter::new(
self.fs.clone(),
block_index,
)?);
Ok(None)
} else {
self.is_done = true;
Ok(None)
}
}
}
impl_result_iter!(TripleIndirectBlockIter, u32);