use crate::io::Read;
use alloc::format;
use alloc::vec;
use crate::block::BlockDevice;
use crate::{Error, Result};
use super::alloc::Alloc;
use super::mdir::Geom;
pub use super::index::pointers;
pub fn payload(geom: &Geom, index: u32) -> u32 {
super::index::payload(geom.block_size, index)
}
pub fn index_of(geom: &Geom, off: u32) -> (u32, u32) {
super::index::index_of(geom.block_size, off)
}
fn read_pointer(dev: &mut dyn BlockDevice, geom: &Geom, block: u32, slot: u32) -> Result<u32> {
if block >= geom.block_count {
return Err(Error::InvalidImage(format!(
"littlefs: file block {block} beyond block count {}",
geom.block_count
)));
}
let mut b = [0u8; 4];
dev.read_at(geom.offset(block) + 4 * slot as u64, &mut b)?;
Ok(u32::from_le_bytes(b))
}
pub fn find(
dev: &mut dyn BlockDevice,
geom: &Geom,
head: u32,
size: u32,
pos: u32,
) -> Result<(u32, u32)> {
if size == 0 {
return Err(Error::InvalidArgument(
"littlefs: seek inside an empty file".into(),
));
}
let (mut current, _) = index_of(geom, size - 1);
let (target, off) = index_of(geom, pos);
let mut head = head;
while current > target {
let (skip, step) = super::index::hop(current, target);
head = read_pointer(dev, geom, head, skip)?;
current -= step;
}
Ok((head, off))
}
pub fn traverse(
dev: &mut dyn BlockDevice,
geom: &Geom,
head: u32,
size: u32,
cb: &mut dyn FnMut(u32),
) -> Result<()> {
if size == 0 {
return Ok(());
}
let (mut index, _) = index_of(geom, size - 1);
let mut head = head;
loop {
cb(head);
if index == 0 {
return Ok(());
}
let count = 2 - (index & 1);
let mut heads = [0u32; 2];
for (i, h) in heads.iter_mut().enumerate().take(count as usize) {
*h = read_pointer(dev, geom, head, i as u32)?;
}
for h in heads.iter().take(count as usize - 1) {
cb(*h);
}
head = heads[count as usize - 1];
index -= count;
}
}
pub fn block_start(geom: &Geom, index: u32) -> u32 {
super::index::block_start(geom.block_size, index)
}
pub trait ChunkSource {
fn fill(&mut self, dev: &mut dyn BlockDevice, off: u64, buf: &mut [u8]) -> Result<()>;
}
pub struct ReaderSource<'r> {
pub body: &'r mut dyn Read,
}
impl ChunkSource for ReaderSource<'_> {
fn fill(&mut self, _dev: &mut dyn BlockDevice, _off: u64, buf: &mut [u8]) -> Result<()> {
self.body.read_exact(buf).map_err(Error::from)
}
}
#[allow(clippy::too_many_arguments)]
pub fn write_blocks(
dev: &mut dyn BlockDevice,
geom: &Geom,
alloc: &mut Alloc,
mut index: u32,
mut prev: Option<u32>,
mut file_off: u64,
src: &mut dyn ChunkSource,
len: u64,
) -> Result<Option<u32>> {
let bs = geom.block_size as usize;
let mut remaining = len;
let mut image = vec![0xffu8; bs];
while remaining > 0 {
let block = alloc.take()?;
let skips = pointers(index);
image.fill(0xff);
if skips > 0 {
let mut p = prev.ok_or_else(|| {
Error::InvalidArgument("littlefs: skip-list continuation without a head".into())
})?;
for j in 0..skips {
let o = 4 * j as usize;
image[o..o + 4].copy_from_slice(&p.to_le_bytes());
if j + 1 < skips {
p = read_pointer(dev, geom, p, j)?;
}
}
}
let cap = payload(geom, index) as u64;
let n = cap.min(remaining) as usize;
let start = 4 * skips as usize;
src.fill(dev, file_off, &mut image[start..start + n])?;
dev.write_at(geom.offset(block), &image)?;
prev = Some(block);
index += 1;
file_off += n as u64;
remaining -= n as u64;
}
Ok(prev)
}