Skip to main content

rlvgl_fs_sim/
lib.rs

1//! Simulator block device backed by a host file.
2//!
3//! Provides [`SimBlockDevice`], an implementation of [`rlvgl_core::fs::BlockDevice`]
4//! that reads and writes sectors from a disk image stored on the host.
5#![deny(missing_docs)]
6
7#[cfg(feature = "mmap")]
8use memmap2::{MmapMut, MmapOptions};
9use std::fs::File;
10use std::io::{Read, Seek, SeekFrom, Write};
11
12use rlvgl_core::fs::{BlockDevice, FsError};
13
14/// Block device backed by a host file representing a disk image.
15pub struct SimBlockDevice {
16    /// Underlying disk image file.
17    file: File,
18    /// Optional memory mapping of the disk image for faster access.
19    #[cfg(feature = "mmap")]
20    mmap: Option<MmapMut>,
21    /// Logical block size in bytes.
22    block_size: usize,
23    /// Total number of blocks in the disk image.
24    num_blocks: u64,
25}
26
27impl SimBlockDevice {
28    /// Create a new [`SimBlockDevice`] from `file` with the given `block_size`.
29    ///
30    /// The file length must be a multiple of `block_size`.
31    pub fn new(file: File, block_size: usize) -> Result<Self, FsError> {
32        let len = file.metadata().map_err(|_| FsError::Device)?.len();
33        if block_size == 0 || len % block_size as u64 != 0 {
34            return Err(FsError::InvalidPath);
35        }
36        #[cfg(feature = "mmap")]
37        let mmap = unsafe { MmapOptions::new().map_mut(&file).ok() };
38        Ok(Self {
39            file,
40            #[cfg(feature = "mmap")]
41            mmap,
42            block_size,
43            num_blocks: len / block_size as u64,
44        })
45    }
46}
47
48impl BlockDevice for SimBlockDevice {
49    fn read_blocks(&mut self, lba: u64, buf: &mut [u8]) -> Result<(), FsError> {
50        let offset = lba
51            .checked_mul(self.block_size as u64)
52            .ok_or(FsError::Device)?;
53        #[cfg(feature = "mmap")]
54        if let Some(mmap) = self.mmap.as_ref() {
55            let start = offset as usize;
56            let end = start.checked_add(buf.len()).ok_or(FsError::Device)?;
57            buf.copy_from_slice(&mmap[start..end]);
58            return Ok(());
59        }
60        self.file
61            .seek(SeekFrom::Start(offset))
62            .map_err(|_| FsError::Device)?;
63        self.file.read_exact(buf).map_err(|_| FsError::Device)
64    }
65
66    fn write_blocks(&mut self, lba: u64, buf: &[u8]) -> Result<(), FsError> {
67        let offset = lba
68            .checked_mul(self.block_size as u64)
69            .ok_or(FsError::Device)?;
70        #[cfg(feature = "mmap")]
71        if let Some(mmap) = self.mmap.as_mut() {
72            let start = offset as usize;
73            let end = start.checked_add(buf.len()).ok_or(FsError::Device)?;
74            mmap[start..end].copy_from_slice(buf);
75            return Ok(());
76        }
77        self.file
78            .seek(SeekFrom::Start(offset))
79            .map_err(|_| FsError::Device)?;
80        self.file.write_all(buf).map_err(|_| FsError::Device)
81    }
82
83    fn block_size(&self) -> usize {
84        self.block_size
85    }
86
87    fn num_blocks(&self) -> u64 {
88        self.num_blocks
89    }
90
91    fn flush(&mut self) -> Result<(), FsError> {
92        #[cfg(feature = "mmap")]
93        if let Some(mmap) = self.mmap.as_mut() {
94            mmap.flush().map_err(|_| FsError::Device)?;
95        }
96        self.file.flush().map_err(|_| FsError::Device)
97    }
98}