use crate::{Error, Result};
use std::io::{Read, Seek, SeekFrom, Write};
pub trait BlockDevice: Send {
fn block_size(&self) -> u32;
fn block_count(&self) -> u64;
fn read_blocks(&mut self, buf: &mut [u8], block_id: u64, block_count: u32) -> Result<()>;
fn write_blocks(&mut self, buf: &[u8], block_id: u64, block_count: u32) -> Result<()>;
fn open(&mut self) -> Result<()> {
Ok(())
}
fn close(&mut self) -> Result<()> {
Ok(())
}
}
pub struct IoBlockDevice<T> {
inner: T,
block_size: u32,
block_count: u64,
}
impl<T> IoBlockDevice<T>
where
T: Read + Write + Seek,
{
pub fn new(inner: T, block_size: u32, total_size: u64) -> Self {
let block_count = total_size / block_size as u64;
Self {
inner,
block_size,
block_count,
}
}
pub fn inner(&self) -> &T {
&self.inner
}
pub fn inner_mut(&mut self) -> &mut T {
&mut self.inner
}
pub fn into_inner(self) -> T {
self.inner
}
}
impl<T> BlockDevice for IoBlockDevice<T>
where
T: Read + Write + Seek + Send,
{
fn block_size(&self) -> u32 {
self.block_size
}
fn block_count(&self) -> u64 {
self.block_count
}
fn read_blocks(&mut self, buf: &mut [u8], block_id: u64, block_count: u32) -> Result<()> {
let offset = block_id * self.block_size as u64;
let size = block_count as usize * self.block_size as usize;
if buf.len() < size {
return Err(Error::InvalidConfig(format!(
"buffer too small: need {} bytes, got {}",
size,
buf.len()
)));
}
self.inner.seek(SeekFrom::Start(offset))?;
self.inner.read_exact(&mut buf[..size])?;
Ok(())
}
fn write_blocks(&mut self, buf: &[u8], block_id: u64, block_count: u32) -> Result<()> {
let offset = block_id * self.block_size as u64;
let size = block_count as usize * self.block_size as usize;
if buf.len() < size {
return Err(Error::InvalidConfig(format!(
"buffer too small: need {} bytes, got {}",
size,
buf.len()
)));
}
self.inner.seek(SeekFrom::Start(offset))?;
self.inner.write_all(&buf[..size])?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::Cursor;
#[test]
fn test_io_block_device_creation() {
let data = vec![0u8; 4096];
let cursor = Cursor::new(data);
let device = IoBlockDevice::new(cursor, 512, 4096);
assert_eq!(device.block_size(), 512);
assert_eq!(device.block_count(), 8); }
#[test]
fn test_io_block_device_write_read() {
let data = vec![0u8; 4096];
let cursor = Cursor::new(data);
let mut device = IoBlockDevice::new(cursor, 512, 4096);
let write_buf = vec![0xAB; 512];
device.write_blocks(&write_buf, 0, 1).unwrap();
let mut read_buf = vec![0u8; 512];
device.read_blocks(&mut read_buf, 0, 1).unwrap();
assert_eq!(read_buf, write_buf);
}
#[test]
fn test_io_block_device_write_read_multiple_blocks() {
let data = vec![0u8; 4096];
let cursor = Cursor::new(data);
let mut device = IoBlockDevice::new(cursor, 512, 4096);
let write_buf = vec![0xCD; 1024];
device.write_blocks(&write_buf, 2, 2).unwrap();
let mut read_buf = vec![0u8; 1024];
device.read_blocks(&mut read_buf, 2, 2).unwrap();
assert_eq!(read_buf, write_buf);
let mut zero_buf = vec![0u8; 512];
device.read_blocks(&mut zero_buf, 0, 1).unwrap();
assert_eq!(zero_buf, vec![0u8; 512]);
}
#[test]
fn test_io_block_device_buffer_too_small() {
let data = vec![0u8; 4096];
let cursor = Cursor::new(data);
let mut device = IoBlockDevice::new(cursor, 512, 4096);
let mut small_buf = vec![0u8; 512];
let result = device.read_blocks(&mut small_buf, 0, 2);
assert!(result.is_err());
let small_write_buf = vec![0u8; 512];
let result = device.write_blocks(&small_write_buf, 0, 2);
assert!(result.is_err());
}
#[test]
fn test_io_block_device_into_inner() {
let data = vec![0u8; 1024];
let cursor = Cursor::new(data);
let device = IoBlockDevice::new(cursor, 512, 1024);
let recovered = device.into_inner();
assert_eq!(recovered.into_inner().len(), 1024);
}
#[test]
fn test_block_device_open_close_default() {
let data = vec![0u8; 1024];
let cursor = Cursor::new(data);
let mut device = IoBlockDevice::new(cursor, 512, 1024);
assert!(device.open().is_ok());
assert!(device.close().is_ok());
}
}