file_system/devices/
memory_device.rs

1//! In-memory device implementation for testing and simulation.
2
3//!
4//! This module provides a memory-based device implementation that stores data
5//! in RAM instead of on physical storage. It's primarily used for testing,
6//! simulation, and development purposes where you need a device that behaves
7//! like storage but doesn't require actual hardware.
8
9use core::fmt::Debug;
10
11use alloc::vec::Vec;
12use alloc::{boxed::Box, vec};
13use shared::AnyByLayout;
14use synchronization::{blocking_mutex::raw::CriticalSectionRawMutex, rwlock::RwLock};
15
16use crate::block_device::GET_BLOCK_SIZE;
17use crate::{
18    ControlCommand, DirectBaseOperations, DirectBlockDevice, Error, MountOperations, Result, Size,
19    block_device,
20};
21
22/// In-memory device implementation with configurable block size.
23///
24/// This device stores all data in memory using a `Vec<u8>` and provides the same
25/// interface as physical storage devices. It's thread-safe and supports all standard
26/// device operations. The block size is configurable at compile time through the
27/// const generic parameter.
28///
29/// # Type Parameters
30///
31/// * `Block_size` - The block size in bytes (must be a power of 2, typically 512)
32///
33/// # Examples
34///
35/// ```rust
36/// extern crate alloc;
37/// use file_system::{MemoryDevice, DirectBaseOperations, Position};
38///
39/// // Create a 1MB memory device with 512-byte blocks
40/// let device = MemoryDevice::<512>::new(1024 * 1024);
41///
42/// // Write some data
43/// let data = b"Hello, Memory Device!";
44/// device.write(data, 0).unwrap();
45///
46/// // Reset position and read back
47/// device.set_position(0, &Position::Start(0)).unwrap();
48/// let mut buffer = alloc::vec![0u8; data.len()];
49/// device.read(&mut buffer, 0).unwrap();
50/// assert_eq!(&buffer, data);
51/// ```
52///
53/// # Thread Safety
54///
55/// The device uses an `RwLock` to ensure thread-safe access to the underlying data.
56/// Multiple readers can access the device simultaneously, but writes are exclusive.
57pub struct MemoryDevice<const BLOCK_SIZE: u32>(RwLock<CriticalSectionRawMutex, Vec<u8>>);
58
59impl<const BLOCK_SIZE: u32> Debug for MemoryDevice<BLOCK_SIZE> {
60    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
61        f.debug_struct("MemoryDevice")
62            .field("size", &self.0.try_read().map(|data| data.len()))
63            .finish()
64    }
65}
66
67impl<const BLOCK_SIZE: u32> MemoryDevice<BLOCK_SIZE> {
68    /// Create a new memory device with the specified size.
69    ///
70    /// The device will be initialized with zeros and have the specified total size.
71    /// The size must be a multiple of the block size.
72    ///
73    /// # Arguments
74    ///
75    /// * `Size` - Total size of the device in bytes
76    ///
77    /// # Panics
78    ///
79    /// Panics if `Size` is not a multiple of `Block_size`.
80    ///
81    /// # Examples
82    ///
83    /// ```rust
84    /// # extern crate alloc;
85    /// # use file_system::MemoryDevice;
86    ///
87    /// // Create a 4KB device with 512-byte blocks
88    /// let device = MemoryDevice::<512>::new(4096);
89    /// ```
90    pub fn new(size: usize) -> Self {
91        assert!(size.is_multiple_of(BLOCK_SIZE as usize));
92
93        let data: Vec<u8> = vec![0; size];
94
95        Self(RwLock::new(data))
96    }
97
98    pub fn new_static(size: usize) -> &'static Self {
99        Box::leak(Box::new(Self::new(size)))
100    }
101
102    /// Create a memory device from existing data.
103    ///
104    /// This allows you to create a device with pre-populated data, useful for
105    /// testing with known data patterns or loading device images.
106    ///
107    /// # Arguments
108    ///
109    /// * `Data` - Vector containing the initial device data
110    ///
111    /// # Panics
112    ///
113    /// Panics if the data length is not a multiple of `Block_size`.
114    ///
115    /// # Examples
116    ///
117    /// ```rust
118    /// # extern crate alloc;
119    /// # use file_system::MemoryDevice;
120    /// # use alloc::vec;
121    ///
122    /// // Create device with specific data
123    /// let data = vec![0x42; 1024]; // 1KB of 0x42 bytes
124    /// let device = MemoryDevice::<512>::from_vec(data);
125    /// ```
126    pub fn from_vec(data: Vec<u8>) -> Self {
127        assert!(data.len().is_multiple_of(BLOCK_SIZE as usize));
128
129        Self(RwLock::new(data))
130    }
131}
132
133impl<const BLOCK_SIZE: u32> DirectBaseOperations for MemoryDevice<BLOCK_SIZE> {
134    /// Read data from the memory device.
135    ///
136    /// Reads data from the current position into the provided buffer.
137    /// The position is automatically advanced by the number of bytes read.
138    fn read(&self, buffer: &mut [u8], absolute_position: Size) -> Result<usize> {
139        let inner = self
140            .0
141            .try_write()
142            .map_err(|_| crate::Error::RessourceBusy)?;
143
144        let absolute_position = absolute_position as usize;
145
146        let read_size = buffer
147            .len()
148            .min(inner.len().saturating_sub(absolute_position));
149        buffer[..read_size]
150            .copy_from_slice(&inner[absolute_position..absolute_position + read_size]);
151        Ok(read_size as _)
152    }
153
154    fn write(&self, buffer: &[u8], absolute_position: Size) -> Result<usize> {
155        let mut inner = self
156            .0
157            .try_write()
158            .map_err(|_| crate::Error::RessourceBusy)?;
159
160        let absolute_position = absolute_position as usize;
161
162        let write_size = buffer
163            .len()
164            .min(inner.len().saturating_sub(absolute_position));
165        inner[absolute_position..absolute_position + write_size]
166            .copy_from_slice(&buffer[..write_size]);
167
168        Ok(write_size as _)
169    }
170
171    fn control(
172        &self,
173        command: crate::ControlCommandIdentifier,
174        _: &AnyByLayout,
175        output: &mut AnyByLayout,
176    ) -> Result<()> {
177        match command {
178            GET_BLOCK_SIZE::IDENTIFIER => {
179                let output = GET_BLOCK_SIZE::cast_output(output)?;
180                *output = BLOCK_SIZE;
181            }
182            block_device::GET_BLOCK_COUNT::IDENTIFIER => {
183                let output = block_device::GET_BLOCK_COUNT::cast_output(output)?;
184
185                *output = (self
186                    .0
187                    .try_read()
188                    .map_err(|_| crate::Error::RessourceBusy)?
189                    .len()
190                    / BLOCK_SIZE as usize) as u32;
191            }
192            _ => return Err(Error::UnsupportedOperation),
193        }
194
195        Ok(())
196    }
197}
198
199impl<const BLOCK_SIZE: u32> MountOperations for MemoryDevice<BLOCK_SIZE> {}
200
201impl<const BLOCK_SIZE: u32> DirectBlockDevice for MemoryDevice<BLOCK_SIZE> {}
202
203#[cfg(test)]
204mod tests {
205    use super::*;
206    use crate::implement_block_device_tests;
207
208    implement_block_device_tests!(MemoryDevice::<512>::new(4096));
209}