use crate::Result;
pub trait Device: Send {
fn size_bytes(&self) -> Result<u64>;
fn read_at(&mut self, offset: u64, buf: &mut [u8]) -> Result<()>;
fn write_at(&mut self, offset: u64, buf: &[u8]) -> Result<()>;
fn sync(&mut self) -> Result<()>;
fn describe(&self) -> String;
}
pub fn read_vec<D: Device + ?Sized>(dev: &mut D, offset: u64, len: usize) -> Result<Vec<u8>> {
let mut buf = vec![0u8; len];
dev.read_at(offset, &mut buf)?;
Ok(buf)
}
pub fn write_and_verify<D: Device + ?Sized>(
dev: &mut D,
offset: u64,
buf: &[u8],
) -> Result<()> {
dev.write_at(offset, buf)?;
let mut check = vec![0u8; buf.len()];
dev.read_at(offset, &mut check)?;
if check != buf {
let first_bad = check
.iter()
.zip(buf.iter())
.position(|(a, b)| a != b)
.unwrap_or(0);
return Err(crate::Error::VerifyMismatch {
offset: offset + first_bad as u64,
expected: buf[first_bad..(first_bad + 16).min(buf.len())].to_vec(),
actual: check[first_bad..(first_bad + 16).min(check.len())].to_vec(),
});
}
Ok(())
}
pub struct MemoryDevice {
pub bytes: Vec<u8>,
pub label: String,
}
impl MemoryDevice {
pub fn new(size: u64) -> Self {
Self {
bytes: vec![0u8; size as usize],
label: format!("in-memory device ({size} bytes)"),
}
}
}
impl Device for MemoryDevice {
fn size_bytes(&self) -> Result<u64> {
Ok(self.bytes.len() as u64)
}
fn read_at(&mut self, offset: u64, buf: &mut [u8]) -> Result<()> {
let end = offset as usize + buf.len();
if end > self.bytes.len() {
return Err(crate::Error::Io(std::io::Error::new(
std::io::ErrorKind::UnexpectedEof,
"read past end of memory device",
)));
}
buf.copy_from_slice(&self.bytes[offset as usize..end]);
Ok(())
}
fn write_at(&mut self, offset: u64, buf: &[u8]) -> Result<()> {
let end = offset as usize + buf.len();
if end > self.bytes.len() {
return Err(crate::Error::Io(std::io::Error::new(
std::io::ErrorKind::UnexpectedEof,
"write past end of memory device",
)));
}
self.bytes[offset as usize..end].copy_from_slice(buf);
Ok(())
}
fn sync(&mut self) -> Result<()> {
Ok(())
}
fn describe(&self) -> String {
self.label.clone()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn memory_device_round_trip() {
let mut d = MemoryDevice::new(4096);
write_and_verify(&mut d, 100, b"hello world").unwrap();
assert_eq!(&d.bytes[100..111], b"hello world");
}
#[test]
fn memory_device_rejects_past_end() {
let mut d = MemoryDevice::new(16);
assert!(d.write_at(8, b"more than 8 bytes").is_err());
}
}