use core::cell::RefCell;
use alloc::{rc::Rc, vec, vec::Vec};
use embedded_sdmmc::{
Block, BlockCount, BlockDevice, BlockIdx, TimeSource, Timestamp, VolumeManager,
};
use crate::mbr;
#[derive(Default, Clone)]
pub struct Device {
bytes: Rc<RefCell<Vec<u8>>>,
resize_chunk: usize,
min_len: usize,
max_blocks: usize,
}
impl Device {
pub const MIN_SIZE: usize = 2_141_184;
pub const MAX_SIZE: usize = 2 * 1024 * 1024 * 1024;
pub fn new(size: usize) -> anyhow::Result<Self> {
anyhow::ensure!(
Self::MIN_SIZE <= size,
"the provided amount of bytes `{size}` is below the lower limit of `{}`",
Self::MIN_SIZE
);
anyhow::ensure!(
size <= Self::MAX_SIZE,
"the provided amount of bytes `{size}` is above the upper limit of `{}`",
Self::MAX_SIZE
);
let bytes = mbr::create_mbr_with_fat(size)?;
Ok(Self {
bytes: Rc::new(RefCell::new(bytes)),
resize_chunk: 0,
min_len: 0,
max_blocks: 0,
})
}
pub fn try_to_raw_bytes(&self) -> anyhow::Result<Vec<u8>> {
Ok(self.bytes.try_borrow()?.clone())
}
pub fn from_raw_bytes_unchecked(bytes: Vec<u8>) -> Self {
Self {
bytes: Rc::new(RefCell::new(bytes)),
resize_chunk: 0,
min_len: 0,
max_blocks: 0,
}
}
pub fn try_to_bytes(&self) -> anyhow::Result<Vec<u8>> {
let bytes = self.bytes.try_borrow()?;
let mut buffer = vec![0u8; bytes.len() + 24];
buffer[0..8].copy_from_slice(&(self.resize_chunk as u64).to_le_bytes());
buffer[8..16].copy_from_slice(&(self.min_len as u64).to_le_bytes());
buffer[16..24].copy_from_slice(&(self.max_blocks as u64).to_le_bytes());
buffer[24..].copy_from_slice(&bytes);
Ok(buffer)
}
pub fn try_from_bytes(buffer: &[u8]) -> anyhow::Result<Self> {
anyhow::ensure!(buffer.len() > 24, "buffer is too small.");
let mut resize_chunk = [0u8; 8];
let mut min_len = [0u8; 8];
let mut max_blocks = [0u8; 8];
resize_chunk.copy_from_slice(&buffer[..8]);
min_len.copy_from_slice(&buffer[8..16]);
max_blocks.copy_from_slice(&buffer[16..24]);
let bytes = buffer[24..].to_vec();
Ok(Self {
bytes: Rc::new(RefCell::new(bytes)),
resize_chunk: u64::from_le_bytes(resize_chunk) as usize,
min_len: u64::from_le_bytes(min_len) as usize,
max_blocks: u64::from_le_bytes(max_blocks) as usize,
})
}
pub fn with_min_len(mut self, min_len: usize) -> Self {
self.min_len = min_len;
self
}
pub fn with_max_len(mut self, max_len: usize) -> Self {
self.max_blocks = max_len / Block::LEN;
self
}
pub fn with_resize_chunk(mut self, resize_chunk: usize) -> Self {
self.resize_chunk = resize_chunk;
self
}
pub fn open(self) -> VolumeManager<Self, Clock> {
VolumeManager::new(self, Clock::default())
}
}
impl BlockDevice for Device {
type Error = anyhow::Error;
fn read(
&self,
blocks: &mut [Block],
start_block_idx: BlockIdx,
reason: &str,
) -> anyhow::Result<()> {
let idx = start_block_idx.0 as usize;
let idx = idx
.checked_mul(Block::LEN)
.ok_or_else(|| anyhow::anyhow!("block idx overflow: {reason}"))?;
let bytes = self
.bytes
.try_borrow()
.map_err(|e| anyhow::anyhow!("device blocked `{e}`: {reason}"))?;
for i in 0..blocks.len() {
let ofs = idx + i * Block::LEN;
let end = ofs + Block::LEN;
if end <= bytes.len() {
blocks[i].contents.copy_from_slice(&bytes[ofs..end]);
} else {
blocks[i].contents.fill(0);
}
}
Ok(())
}
fn write(&self, blocks: &[Block], start_block_idx: BlockIdx) -> anyhow::Result<()> {
let idx = start_block_idx.0 as usize;
let idx = idx
.checked_mul(Block::LEN)
.ok_or_else(|| anyhow::anyhow!("block idx overflow"))?;
let len = blocks
.len()
.checked_mul(Block::LEN)
.ok_or_else(|| anyhow::anyhow!("block len overflow"))?;
let last = idx
.checked_add(len)
.ok_or_else(|| anyhow::anyhow!("block last overflow"))?;
let mut bytes = self.bytes.try_borrow_mut()?;
if last > bytes.len() {
anyhow::ensure!(
self.max_blocks == 0 || last <= self.max_blocks * Block::LEN,
"write attempt to overflow maximum length"
);
let last = last.max(self.min_len).max(bytes.len() + self.resize_chunk);
bytes.resize(last, 0);
}
for i in 0..blocks.len() {
let ofs = idx + i * Block::LEN;
bytes[ofs..ofs + Block::LEN].copy_from_slice(&blocks[i].contents);
}
Ok(())
}
fn num_blocks(&self) -> anyhow::Result<BlockCount> {
let len = self.bytes.try_borrow()?.len();
let len = len / Block::LEN;
Ok(BlockCount(len as u32))
}
}
#[derive(Debug)]
pub struct Clock {
ts: RefCell<Timestamp>,
def: Timestamp,
}
impl Default for Clock {
fn default() -> Self {
let ts = Timestamp::from_calendar(1980, 01, 01, 13, 30, 05).expect("invalid initial ts");
Self {
ts: RefCell::new(ts),
def: ts,
}
}
}
impl TimeSource for Clock {
fn get_timestamp(&self) -> Timestamp {
let ts = { self.ts.try_borrow().map(|t| *t).unwrap_or(self.def) };
{
let mut tsp = ts;
tsp.seconds += 1;
if tsp.seconds == 60 {
tsp.minutes += 1;
tsp.seconds = 0;
}
if tsp.minutes == 60 {
tsp.hours += 1;
tsp.minutes = 0;
}
if tsp.hours == 24 {
tsp.zero_indexed_day += 1;
tsp.hours = 0;
}
if tsp.zero_indexed_day == 27 {
tsp.zero_indexed_month += 1;
tsp.zero_indexed_day = 0;
}
if tsp.zero_indexed_month == 12 {
tsp.year_since_1970 += 1;
tsp.zero_indexed_month = 0;
}
if let Ok(mut t) = self.ts.try_borrow_mut() {
*t = tsp;
}
}
ts
}
}