use alloc::boxed::Box;
use alloc::vec;
use core::fmt;
use core::ops;
use bonsai_disk::Disk;
use embedded_io::ErrorType;
use generic_array::ConstArrayLength;
use generic_array::IntoArrayLength;
use generic_array::typenum::Const;
use crate::IecNumber;
use crate::MetricNumber;
extern crate alloc;
#[derive(Clone, Copy, Default)]
pub struct AccessStats {
pub reads: usize,
pub read_bytes: usize,
pub writes: usize,
pub write_bytes: usize,
pub erases: usize,
}
impl ops::Add for AccessStats {
type Output = Self;
fn add(self, other: Self) -> Self {
Self {
reads: self.reads + other.reads,
read_bytes: self.read_bytes + other.read_bytes,
writes: self.writes + other.writes,
write_bytes: self.write_bytes + other.write_bytes,
erases: self.erases + other.erases,
}
}
}
impl ops::Sub for AccessStats {
type Output = Self;
fn sub(self, other: Self) -> Self {
Self {
reads: self.reads.saturating_sub(other.reads),
read_bytes: self.read_bytes.saturating_sub(other.read_bytes),
writes: self.writes.saturating_sub(other.writes),
write_bytes: self.write_bytes.saturating_sub(other.write_bytes),
erases: self.erases.saturating_sub(other.erases),
}
}
}
impl ops::AddAssign for AccessStats {
fn add_assign(&mut self, other: Self) {
*self = self.clone() + other;
}
}
impl ops::SubAssign for AccessStats {
fn sub_assign(&mut self, other: Self) {
*self = self.clone() - other;
}
}
impl ops::Mul<usize> for AccessStats {
type Output = Self;
fn mul(self, rhs: usize) -> Self {
Self {
reads: self.reads * rhs,
read_bytes: self.read_bytes * rhs,
writes: self.writes * rhs,
write_bytes: self.write_bytes * rhs,
erases: self.erases * rhs,
}
}
}
impl ops::Div<usize> for AccessStats {
type Output = Self;
fn div(self, rhs: usize) -> Self {
Self {
reads: self.reads / rhs,
read_bytes: self.read_bytes / rhs,
writes: self.writes / rhs,
write_bytes: self.write_bytes / rhs,
erases: self.erases / rhs,
}
}
}
impl ops::MulAssign<usize> for AccessStats {
fn mul_assign(&mut self, rhs: usize) {
*self = self.clone() * rhs;
}
}
impl ops::DivAssign<usize> for AccessStats {
fn div_assign(&mut self, rhs: usize) {
*self = self.clone() / rhs;
}
}
impl fmt::Debug for AccessStats {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.writes == 0 && self.write_bytes == 0 && self.erases == 0 {
f.debug_struct("ReadStats")
.field("reads", &self.reads)
.field("read_bytes", &self.read_bytes)
.finish()
} else {
f.debug_struct("AccessStats")
.field("reads", &self.reads)
.field("read_bytes", &self.read_bytes)
.field("writes", &self.writes)
.field("write_bytes", &self.write_bytes)
.field("erases", &self.erases)
.finish()
}
}
}
impl fmt::Display for AccessStats {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.writes == 0 && self.write_bytes == 0 && self.erases == 0 {
write!(
f,
"reads {}, {}B",
MetricNumber(self.reads),
IecNumber(self.read_bytes)
)
} else {
write!(
f,
"reads {}, {}B, writes {}, {}B, erases {} pages",
MetricNumber(self.reads),
IecNumber(self.read_bytes),
MetricNumber(self.writes),
IecNumber(self.write_bytes),
MetricNumber(self.erases),
)
}
}
}
#[derive(Debug, Clone)]
pub struct RamDisk<const BLOCK_SIZE: usize, const WRITE_SIZE: usize> {
pub data: Box<[u8]>,
pub stats: AccessStats,
}
impl<const BLOCK_SIZE: usize, const WRITE_SIZE: usize> RamDisk<BLOCK_SIZE, WRITE_SIZE> {
pub fn new(blocks: usize) -> Self {
Self {
data: vec![0; blocks * BLOCK_SIZE].into_boxed_slice(),
stats: AccessStats::default(),
}
}
pub fn new_erased(blocks: usize) -> Self {
Self {
data: vec![0xFF; blocks * BLOCK_SIZE].into_boxed_slice(),
stats: AccessStats::default(),
}
}
pub fn clear_stats(&mut self) {
self.stats = AccessStats::default();
}
}
impl<const BLOCK_SIZE: usize, const WRITE_SIZE: usize> AsMut<Self>
for RamDisk<BLOCK_SIZE, WRITE_SIZE>
{
fn as_mut(&mut self) -> &mut Self {
self
}
}
impl<const BLOCK_SIZE: usize, const WRITE_SIZE: usize> ErrorType
for RamDisk<BLOCK_SIZE, WRITE_SIZE>
{
type Error = core::convert::Infallible;
}
impl<const BLOCK_SIZE: usize, const WRITE_SIZE: usize> Disk for RamDisk<BLOCK_SIZE, WRITE_SIZE>
where
Const<WRITE_SIZE>: IntoArrayLength,
{
type WRITE_GRANULARITY = ConstArrayLength<WRITE_SIZE>;
const ERASE_BLOCK_SIZE: usize = BLOCK_SIZE;
#[track_caller]
fn read(&mut self, offset: usize, buf: &mut [u8]) -> Result<usize, Self::Error> {
assert!(BLOCK_SIZE % WRITE_SIZE == 0);
assert!(BLOCK_SIZE >= WRITE_SIZE);
assert!(
offset % WRITE_SIZE == 0,
"Unaligned read: {offset} % {WRITE_SIZE}"
);
assert!(
offset + buf.len() <= self.data.len(),
"Read out of bounds: {offset} + {} > {}",
buf.len(),
self.data.len()
);
if buf.len() == 0 {
return Ok(0);
}
assert!(
buf.len() >= WRITE_SIZE,
"Buffer too small: {} < {WRITE_SIZE}",
buf.len()
);
let buf_len = buf.len() / WRITE_SIZE * WRITE_SIZE;
let block_end = ((offset / BLOCK_SIZE) + 1) * BLOCK_SIZE;
let read_end = usize::min(block_end, offset + buf_len);
let read_len = read_end - offset;
buf[..read_len].copy_from_slice(&self.data[offset..read_end]);
self.stats.reads += 1;
self.stats.read_bytes += read_len;
Ok(read_len)
}
#[track_caller]
fn write(&mut self, offset: usize, buf: &[u8]) -> Result<usize, Self::Error> {
assert!(BLOCK_SIZE % WRITE_SIZE == 0);
assert!(BLOCK_SIZE >= WRITE_SIZE);
assert!(
offset % WRITE_SIZE == 0,
"Unaligned write: {offset} % {WRITE_SIZE}"
);
assert!(
offset + buf.len() <= self.data.len(),
"Write out of bounds: {offset} + {} > {}",
buf.len(),
self.data.len()
);
if buf.len() == 0 {
return Ok(0);
}
assert!(
buf.len() >= WRITE_SIZE,
"Buffer too small: {} < {WRITE_SIZE}",
buf.len()
);
let buf_len = buf.len() / WRITE_SIZE * WRITE_SIZE;
let block_end = ((offset / BLOCK_SIZE) + 1) * BLOCK_SIZE;
let write_end = usize::min(block_end, offset + buf_len);
let write_len = write_end - offset;
for i in 0..write_len {
self.data[offset + i] &= buf[i];
}
self.stats.writes += 1;
self.stats.write_bytes += write_len;
Ok(write_len)
}
#[track_caller]
fn erase(&mut self, block: usize) -> Result<(), Self::Error> {
assert!(BLOCK_SIZE % WRITE_SIZE == 0);
assert!(BLOCK_SIZE >= WRITE_SIZE);
let offset = block * BLOCK_SIZE;
let len = BLOCK_SIZE;
for i in (offset)..(offset + len) {
self.data[i] = 0xFF;
}
self.stats.erases += 1;
Ok(())
}
#[track_caller]
fn block_count(&self) -> usize {
assert!(BLOCK_SIZE % WRITE_SIZE == 0);
assert!(BLOCK_SIZE >= WRITE_SIZE);
debug_assert!(self.data.len() % BLOCK_SIZE == 0);
self.data.len() / BLOCK_SIZE
}
}
#[cfg(test)]
mod test {
use super::*;
const BLOCK_SIZE: usize = 512;
const WRITE_SIZE: usize = 8;
#[test]
fn test_ramdisk_simple_write() {
let mut disk = RamDisk::<BLOCK_SIZE, WRITE_SIZE>::new_erased(4);
let write_data = [1, 2, 3, 4, 5, 6, 7, 8];
let write_result = disk.write(2 * WRITE_SIZE, &write_data);
assert!(write_result.is_ok());
assert_eq!(write_result.unwrap(), write_data.len());
for i in 0..write_data.len() {
assert_eq!(disk.data[2 * WRITE_SIZE + i], write_data[i]);
}
assert_eq!(disk.stats.writes, 1);
assert_eq!(disk.stats.write_bytes, write_data.len());
assert_eq!(disk.stats.reads, 0);
assert_eq!(disk.stats.read_bytes, 0);
assert_eq!(disk.stats.erases, 0);
}
#[test]
fn test_ramdisk_partial_write() {
let mut disk = RamDisk::<BLOCK_SIZE, WRITE_SIZE>::new_erased(4);
let write_data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
let write_result = disk.write(2 * WRITE_SIZE, &write_data);
assert!(write_result.is_ok());
assert_eq!(write_result.unwrap(), 8);
for i in 0..8 {
assert_eq!(disk.data[2 * WRITE_SIZE + i], write_data[i]);
}
for i in 8..12 {
assert_eq!(disk.data[2 * WRITE_SIZE + i], 0xFF);
}
assert_eq!(disk.stats.writes, 1);
assert_eq!(disk.stats.write_bytes, 8);
assert_eq!(disk.stats.reads, 0);
assert_eq!(disk.stats.read_bytes, 0);
assert_eq!(disk.stats.erases, 0);
}
#[test]
fn test_ramdisk_simple_read() {
let mut disk = RamDisk::<BLOCK_SIZE, WRITE_SIZE>::new_erased(4);
let direct_data = [10, 20, 30, 40, 50, 60, 70, 80];
for i in 0..direct_data.len() {
disk.data[2 * WRITE_SIZE + i] = direct_data[i];
}
let mut read_data = [0u8; 8];
let read_result = disk.read(2 * WRITE_SIZE, &mut read_data);
assert!(read_result.is_ok());
assert_eq!(read_result.unwrap(), direct_data.len());
assert_eq!(read_data, direct_data);
assert_eq!(disk.stats.reads, 1);
assert_eq!(disk.stats.read_bytes, direct_data.len());
assert_eq!(disk.stats.writes, 0);
assert_eq!(disk.stats.write_bytes, 0);
assert_eq!(disk.stats.erases, 0);
}
#[test]
fn test_ramdisk_partial_read() {
let mut disk = RamDisk::<BLOCK_SIZE, WRITE_SIZE>::new_erased(4);
let direct_data = [10, 20, 30, 40, 50, 60, 70, 80];
for i in 0..direct_data.len() {
disk.data[2 * WRITE_SIZE + i] = direct_data[i];
}
let mut read_data = [0u8; 12];
let read_result = disk.read(2 * WRITE_SIZE, &mut read_data);
assert!(read_result.is_ok());
assert_eq!(read_result.unwrap(), 8);
for i in 0..8 {
assert_eq!(read_data[i], direct_data[i]);
}
for i in 8..12 {
assert_eq!(read_data[i], 0);
}
assert_eq!(disk.stats.reads, 1);
assert_eq!(disk.stats.read_bytes, 8);
assert_eq!(disk.stats.writes, 0);
assert_eq!(disk.stats.write_bytes, 0);
assert_eq!(disk.stats.erases, 0);
}
#[test]
fn test_ramdisk_read_write() {
let mut disk = RamDisk::<BLOCK_SIZE, WRITE_SIZE>::new_erased(4);
let write_data = [1, 2, 3, 4, 5, 6, 7, 8];
let write_result = disk.write(3 * WRITE_SIZE, &write_data);
assert!(write_result.is_ok());
assert_eq!(write_result.unwrap(), write_data.len());
let mut read_data = [0u8; 8];
let read_result = disk.read(3 * WRITE_SIZE, &mut read_data);
assert!(read_result.is_ok());
assert_eq!(read_result.unwrap(), write_data.len());
assert_eq!(read_data, write_data);
assert_eq!(disk.stats.reads, 1);
assert_eq!(disk.stats.read_bytes, write_data.len());
assert_eq!(disk.stats.writes, 1);
assert_eq!(disk.stats.write_bytes, write_data.len());
assert_eq!(disk.stats.erases, 0);
}
#[test]
fn test_ramdisk_single_write_two_reads() {
let mut disk = RamDisk::<BLOCK_SIZE, WRITE_SIZE>::new_erased(4);
let write_data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
let write_result = disk.write(2 * WRITE_SIZE, &write_data);
assert!(write_result.is_ok());
assert_eq!(write_result.unwrap(), write_data.len());
let mut read_data1 = [0u8; 8];
let read_result1 = disk.read(2 * WRITE_SIZE, &mut read_data1);
assert!(read_result1.is_ok());
assert_eq!(read_result1.unwrap(), 8);
assert_eq!(read_data1, [1, 2, 3, 4, 5, 6, 7, 8]);
let mut read_data2 = [0u8; 8];
let read_result2 = disk.read(2 * WRITE_SIZE + 8, &mut read_data2);
assert!(read_result2.is_ok());
assert_eq!(read_result2.unwrap(), 8);
assert_eq!(read_data2, [9, 10, 11, 12, 13, 14, 15, 16]);
assert_eq!(disk.stats.reads, 2);
assert_eq!(disk.stats.read_bytes, 16);
assert_eq!(disk.stats.writes, 1);
assert_eq!(disk.stats.write_bytes, 16);
assert_eq!(disk.stats.erases, 0);
}
#[test]
fn test_ramdisk_two_writes_single_read() {
let mut disk = RamDisk::<BLOCK_SIZE, WRITE_SIZE>::new_erased(4);
let write_data1 = [1, 2, 3, 4, 5, 6, 7, 8];
let write_result1 = disk.write(2 * WRITE_SIZE, &write_data1);
assert!(write_result1.is_ok());
assert_eq!(write_result1.unwrap(), write_data1.len());
let write_data2 = [9, 10, 11, 12, 13, 14, 15, 16];
let write_result2 = disk.write(3 * WRITE_SIZE, &write_data2);
assert!(write_result2.is_ok());
assert_eq!(write_result2.unwrap(), write_data2.len());
let mut read_data = [0u8; 16];
let read_result = disk.read(2 * WRITE_SIZE, &mut read_data);
assert!(read_result.is_ok());
assert_eq!(read_result.unwrap(), 16);
assert_eq!(
read_data,
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
);
assert_eq!(disk.stats.reads, 1);
assert_eq!(disk.stats.read_bytes, 16);
assert_eq!(disk.stats.writes, 2);
assert_eq!(disk.stats.write_bytes, 16);
assert_eq!(disk.stats.erases, 0);
}
#[test]
fn test_ramdisk_repeated_write() {
let mut disk = RamDisk::<BLOCK_SIZE, WRITE_SIZE>::new_erased(4);
let write_data = [1, 2, 3, 4, 5, 6, 7, 8];
let write_result = disk.write(3 * WRITE_SIZE, &write_data);
assert!(write_result.is_ok());
assert_eq!(write_result.unwrap(), write_data.len());
let write_result = disk.write(3 * WRITE_SIZE, &write_data);
assert!(write_result.is_ok());
assert_eq!(write_result.unwrap(), write_data.len());
let mut read_data = [0u8; 8];
let read_result = disk.read(3 * WRITE_SIZE, &mut read_data);
assert!(read_result.is_ok());
assert_eq!(read_result.unwrap(), write_data.len());
assert_eq!(read_data, write_data);
assert_eq!(disk.stats.reads, 1);
assert_eq!(disk.stats.read_bytes, write_data.len());
assert_eq!(disk.stats.writes, 2);
assert_eq!(disk.stats.write_bytes, write_data.len() * 2);
assert_eq!(disk.stats.erases, 0);
}
#[test]
fn test_ramdisk_erase() {
let mut disk = RamDisk::<BLOCK_SIZE, WRITE_SIZE>::new_erased(4);
let write_data = [1, 2, 3, 4, 5, 6, 7, 8];
let write_result = disk.write(3 * WRITE_SIZE, &write_data);
assert!(write_result.is_ok());
assert_eq!(write_result.unwrap(), write_data.len());
let erase_result = disk.erase(0);
assert!(erase_result.is_ok());
let mut read_data = [0u8; 8];
let read_result = disk.read(3 * WRITE_SIZE, &mut read_data);
assert!(read_result.is_ok());
assert_eq!(read_result.unwrap(), write_data.len());
assert_ne!(read_data, write_data);
assert_eq!(disk.stats.reads, 1);
assert_eq!(disk.stats.read_bytes, 8);
assert_eq!(disk.stats.writes, 1);
assert_eq!(disk.stats.write_bytes, 8);
assert_eq!(disk.stats.erases, 1);
}
#[test]
fn test_ramdisk_write_without_erase() {
let mut disk = RamDisk::<BLOCK_SIZE, WRITE_SIZE>::new(4);
let write_garbage = [0; 8];
let write_result = disk.write(3 * WRITE_SIZE, &write_garbage);
assert!(write_result.is_ok());
assert_eq!(write_result.unwrap(), write_garbage.len());
let write_data = [1, 2, 3, 4, 5, 6, 7, 8];
let write_result = disk.write(3 * WRITE_SIZE, &write_data);
assert!(write_result.is_ok());
assert_eq!(write_result.unwrap(), write_data.len());
let mut read_data = [0u8; 8];
let read_result = disk.read(3 * WRITE_SIZE, &mut read_data);
assert!(read_result.is_ok());
assert_eq!(read_result.unwrap(), write_data.len());
assert_ne!(read_data, write_data);
let erase_result = disk.erase(0);
assert!(erase_result.is_ok());
let write_result = disk.write(3 * WRITE_SIZE, &write_data);
assert!(write_result.is_ok());
assert_eq!(write_result.unwrap(), write_data.len());
let read_result = disk.read(3 * WRITE_SIZE, &mut read_data);
assert!(read_result.is_ok());
assert_eq!(read_result.unwrap(), write_data.len());
assert_eq!(read_data, write_data);
assert_eq!(disk.stats.reads, 2);
assert_eq!(disk.stats.read_bytes, 16);
assert_eq!(disk.stats.writes, 3);
assert_eq!(disk.stats.write_bytes, 24);
assert_eq!(disk.stats.erases, 1);
}
#[test]
fn test_ramdisk_block_count() {
let disk = RamDisk::<BLOCK_SIZE, WRITE_SIZE>::new(3);
assert_eq!(disk.block_count(), 3);
}
}