use crate::bd::LfsCache;
use crate::error::LFS_ERR_CORRUPT;
use crate::fs::Lfs;
use crate::types::{lfs_block_t, lfs_off_t, lfs_size_t};
use crate::util::{lfs_aligndown, lfs_alignup, lfs_min};
#[inline(always)]
pub fn lfs_cache_drop(_lfs: *const Lfs, rcache: *mut LfsCache) {
unsafe {
(*rcache).block = crate::types::LFS_BLOCK_NULL;
}
}
#[inline(always)]
pub fn lfs_cache_zero(lfs: *const Lfs, pcache: *mut LfsCache) {
unsafe {
let cfg = (*lfs).cfg;
let cache_size = (*cfg).cache_size as usize;
let buf = (*pcache).buffer;
if !buf.is_null() {
core::ptr::write_bytes(buf, 0xff, cache_size);
}
(*pcache).block = crate::types::LFS_BLOCK_NULL;
}
}
pub fn lfs_bd_read(
lfs: *mut Lfs,
pcache: *const LfsCache,
rcache: *mut LfsCache,
hint: lfs_size_t,
block: lfs_block_t,
off: lfs_off_t,
buffer: *mut u8,
size: lfs_size_t,
) -> i32 {
unsafe {
let lfs = &mut *lfs;
let cfg = &*lfs.cfg;
let read = match cfg.read {
Some(f) => f,
None => return LFS_ERR_CORRUPT,
};
if off + size > cfg.block_size || (lfs.block_count != 0 && block >= lfs.block_count) {
return crate::lfs_err!(LFS_ERR_CORRUPT);
}
let mut data = buffer;
let mut off = off;
let mut size = size;
while size > 0 {
let mut diff = size;
if !pcache.is_null() {
let pcache = &*pcache;
if block == pcache.block && off < pcache.off + pcache.size {
if off >= pcache.off {
diff = lfs_min(diff, pcache.size - (off - pcache.off));
if !pcache.buffer.is_null() {
core::ptr::copy_nonoverlapping(
pcache.buffer.add((off - pcache.off) as usize),
data,
diff as usize,
);
}
data = data.add(diff as usize);
off += diff;
size -= diff;
continue;
}
diff = lfs_min(diff, pcache.off - off);
}
}
let rcache = &mut *rcache;
if block == rcache.block && off < rcache.off + rcache.size {
if off >= rcache.off {
diff = lfs_min(diff, rcache.size - (off - rcache.off));
if !rcache.buffer.is_null() {
core::ptr::copy_nonoverlapping(
rcache.buffer.add((off - rcache.off) as usize),
data,
diff as usize,
);
}
data = data.add(diff as usize);
off += diff;
size -= diff;
continue;
}
diff = lfs_min(diff, rcache.off - off);
}
if size >= hint && off.is_multiple_of(cfg.read_size) && size >= cfg.read_size {
diff = lfs_aligndown(diff, cfg.read_size);
crate::lfs_trace!("bd_read block={} off={} size={}", block, off, diff);
let err = read(cfg as *const _, block, off, data, diff);
crate::lfs_assert!(err <= 0);
if err != 0 {
crate::lfs_trace!("bd_read block={} -> CORRUPT", block);
return crate::lfs_pass_err!(err);
}
data = data.add(diff as usize);
off += diff;
size -= diff;
continue;
}
crate::lfs_assert!(lfs.block_count == 0 || block < lfs.block_count);
rcache.block = block;
rcache.off = lfs_aligndown(off, cfg.read_size);
rcache.size = lfs_min(
lfs_min(lfs_alignup(off + hint, cfg.read_size), cfg.block_size)
.saturating_sub(rcache.off),
cfg.cache_size,
);
crate::lfs_trace!(
"bd_read block={} off={} size={}",
rcache.block,
rcache.off,
rcache.size
);
let err = read(
cfg as *const _,
rcache.block,
rcache.off,
rcache.buffer,
rcache.size,
);
crate::lfs_assert!(err <= 0);
if err != 0 {
crate::lfs_trace!("bd_read block={} -> CORRUPT", rcache.block);
rcache.block = crate::types::LFS_BLOCK_NULL;
return crate::lfs_pass_err!(err);
}
}
0
}
}
pub fn lfs_bd_cmp(
lfs: *mut Lfs,
pcache: *const LfsCache,
rcache: *mut LfsCache,
hint: lfs_size_t,
block: lfs_block_t,
off: lfs_off_t,
buffer: *const u8,
size: lfs_size_t,
) -> i32 {
const LFS_CMP_EQ: i32 = 0;
const LFS_CMP_LT: i32 = 1;
const LFS_CMP_GT: i32 = 2;
let mut i: lfs_off_t = 0;
while i < size {
let mut dat = [0u8; 8];
let diff = lfs_min(size - i, 8) as usize;
let err = lfs_bd_read(
lfs,
pcache,
rcache,
hint.saturating_sub(i),
block,
off + i,
dat.as_mut_ptr(),
diff as lfs_size_t,
);
if err != 0 {
return crate::lfs_pass_err!(err);
}
let res = unsafe {
let disk = &dat[..diff];
let expected = core::slice::from_raw_parts(buffer.add(i as usize), diff);
disk.cmp(expected)
};
match res {
core::cmp::Ordering::Equal => {}
core::cmp::Ordering::Less => return LFS_CMP_LT,
core::cmp::Ordering::Greater => return LFS_CMP_GT,
}
i += diff as lfs_off_t;
}
LFS_CMP_EQ
}
pub fn lfs_bd_crc(
lfs: *mut Lfs,
pcache: *const LfsCache,
rcache: *mut LfsCache,
hint: lfs_size_t,
block: lfs_block_t,
off: lfs_off_t,
size: lfs_size_t,
crc: *mut u32,
) -> i32 {
use crate::crc::lfs_crc;
use crate::util::lfs_min;
let mut i: lfs_off_t = 0;
while i < size {
let mut dat = [0u8; 8];
let diff = lfs_min(size - i, 8) as usize;
let err = lfs_bd_read(
lfs,
pcache,
rcache,
hint.saturating_sub(i),
block,
off + i,
dat.as_mut_ptr(),
diff as lfs_size_t,
);
if err != 0 {
return crate::lfs_pass_err!(err);
}
unsafe {
*crc = lfs_crc(*crc, dat.as_ptr(), diff);
}
i += diff as lfs_off_t;
}
0
}
pub fn lfs_bd_flush(
lfs: *const Lfs,
pcache: *mut LfsCache,
rcache: *mut LfsCache,
validate: bool,
) -> i32 {
use crate::types::LFS_BLOCK_INLINE;
use crate::util::lfs_alignup;
unsafe {
let lfs_ptr = lfs;
let lfs = &*lfs;
let pcache = &mut *pcache;
let cfg = &*lfs.cfg;
if pcache.block != crate::types::LFS_BLOCK_NULL && pcache.block != LFS_BLOCK_INLINE {
crate::lfs_assert!(pcache.block < lfs.block_count);
let diff = lfs_alignup(pcache.size, cfg.prog_size);
crate::lfs_trace!(
"bd_prog block={} off={} size={}",
pcache.block,
pcache.off,
diff
);
let prog = match cfg.prog {
Some(f) => f,
None => return LFS_ERR_CORRUPT,
};
let err = prog(
cfg as *const _,
pcache.block,
pcache.off,
pcache.buffer,
diff,
);
crate::lfs_assert!(err <= 0);
if err != 0 {
crate::lfs_trace!("bd_prog block={} -> CORRUPT", pcache.block);
return crate::lfs_pass_err!(err);
}
if validate {
lfs_cache_drop(lfs, rcache);
let res = lfs_bd_cmp(
lfs_ptr as *mut Lfs,
core::ptr::null(),
rcache,
diff,
pcache.block,
pcache.off,
pcache.buffer,
diff,
);
if res < 0 {
return res;
}
if res != 0 {
return crate::lfs_err!(LFS_ERR_CORRUPT);
}
}
lfs_cache_zero(lfs, pcache);
}
0
}
}
pub fn lfs_bd_sync(
lfs: *const Lfs,
pcache: *mut LfsCache,
rcache: *mut LfsCache,
validate: bool,
) -> i32 {
unsafe {
lfs_cache_drop(lfs, rcache);
let err = lfs_bd_flush(lfs, pcache, rcache, validate);
if err != 0 {
return crate::lfs_pass_err!(err);
}
let cfg = &*(*lfs).cfg;
let sync = match cfg.sync {
Some(f) => f,
None => return LFS_ERR_CORRUPT,
};
let err = sync(cfg as *const _);
crate::lfs_assert!(err <= 0);
err
}
}
pub fn lfs_bd_prog(
lfs: *const Lfs,
pcache: *mut LfsCache,
rcache: *mut LfsCache,
validate: bool,
block: lfs_block_t,
off: lfs_off_t,
buffer: *const u8,
size: lfs_size_t,
) -> i32 {
use crate::types::LFS_BLOCK_INLINE;
use crate::util::{lfs_aligndown, lfs_max, lfs_min};
unsafe {
let lfs = &*lfs;
let cfg = &*lfs.cfg;
let pcache = &mut *pcache;
crate::lfs_assert!(block == LFS_BLOCK_INLINE || block < lfs.block_count);
crate::lfs_assert!(off + size <= cfg.block_size);
let mut data = buffer;
let mut off = off;
let mut size = size;
while size > 0 {
if block == pcache.block && off >= pcache.off && off < pcache.off + cfg.cache_size {
let diff = lfs_min(size, cfg.cache_size - (off - pcache.off));
if !pcache.buffer.is_null() && !data.is_null() {
if (block == 0 || block == 1) && off <= 12 && off + diff > 12 {
let magic_start = 12usize.saturating_sub(off as usize);
let magic_len = (8).min(diff as usize - magic_start);
if magic_len > 0 {
let slice =
core::slice::from_raw_parts(data.add(magic_start), magic_len);
crate::lfs_trace!(
"bd_prog superblock block={} off={} size={} magic_region[{}..{}]={:?}",
block,
off,
size,
magic_start,
magic_start + magic_len,
slice
);
}
}
core::ptr::copy_nonoverlapping(
data,
pcache.buffer.add((off - pcache.off) as usize),
diff as usize,
);
}
data = data.add(diff as usize);
off += diff;
size -= diff;
pcache.size = lfs_max(pcache.size, off - pcache.off);
if pcache.size == cfg.cache_size {
let err = lfs_bd_flush(lfs, pcache, rcache, validate);
if err != 0 {
return crate::lfs_pass_err!(err);
}
}
continue;
}
crate::lfs_assert!(pcache.block == crate::types::LFS_BLOCK_NULL);
pcache.block = block;
pcache.off = lfs_aligndown(off, cfg.prog_size);
pcache.size = 0;
}
0
}
}
pub fn lfs_bd_erase(lfs: *const Lfs, block: lfs_block_t) -> i32 {
unsafe {
let lfs = &*lfs;
crate::lfs_assert!(block < lfs.block_count);
let erase = match (*lfs.cfg).erase {
Some(f) => f,
None => return LFS_ERR_CORRUPT,
};
crate::lfs_trace!("bd_erase block={}", block);
let err = erase(lfs.cfg, block);
crate::lfs_assert!(err <= 0);
if err != 0 {
crate::lfs_trace!("bd_erase block={} -> CORRUPT", block);
}
err
}
}