use crate::block::{BlockDevice, BlockRead};
use crate::error::{Error, Result};
use std::sync::Mutex;
pub(crate) struct Bytes(pub Mutex<Vec<u8>>);
impl Bytes {
pub(crate) fn new(bytes: Vec<u8>) -> Self {
Bytes(Mutex::new(bytes))
}
}
impl BlockRead for Bytes {
fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<()> {
let b = self.0.lock().unwrap();
read_into(&b, offset, buf)
}
fn size_bytes(&self) -> u64 {
self.0.lock().unwrap().len() as u64
}
}
pub(crate) struct RwBytes(pub Mutex<Vec<u8>>);
impl RwBytes {
pub(crate) fn new(bytes: Vec<u8>) -> Self {
RwBytes(Mutex::new(bytes))
}
}
impl BlockRead for RwBytes {
fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<()> {
let b = self.0.lock().unwrap();
read_into(&b, offset, buf)
}
fn size_bytes(&self) -> u64 {
self.0.lock().unwrap().len() as u64
}
}
impl BlockDevice for RwBytes {
fn write_at(&self, offset: u64, buf: &[u8]) -> Result<()> {
let mut b = self.0.lock().unwrap();
let start = offset as usize;
let end = start + buf.len();
if end > b.len() {
return Err(Error::ShortRead {
offset,
want: buf.len(),
got: b.len().saturating_sub(start),
});
}
b[start..end].copy_from_slice(buf);
Ok(())
}
fn is_writable(&self) -> bool {
true
}
}
fn read_into(b: &[u8], offset: u64, buf: &mut [u8]) -> Result<()> {
let start = offset as usize;
let end = start + buf.len();
if end > b.len() {
return Err(Error::ShortRead {
offset,
want: buf.len(),
got: b.len().saturating_sub(start),
});
}
buf.copy_from_slice(&b[start..end]);
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_read_past_the_end_is_a_short_read_not_a_panic() {
let dev = Bytes::new(vec![0xAB; 8]);
let mut buf = [0u8; 16];
match dev.read_at(0, &mut buf).expect_err("past the end") {
Error::ShortRead { offset, want, got } => assert_eq!((offset, want, got), (0, 16, 8)),
other => panic!("expected ShortRead, got {other:?}"),
}
assert_eq!(buf, [0u8; 16], "a refused read leaves the buffer alone");
}
#[test]
fn the_writable_one_reads_the_same_way() {
let dev = RwBytes::new(vec![0xAB; 8]);
let mut buf = [0u8; 16];
assert!(dev.read_at(0, &mut buf).is_err());
assert!(dev.is_writable());
}
#[test]
fn a_write_past_the_end_is_refused_rather_than_growing_the_buffer() {
let dev = RwBytes::new(vec![0u8; 4]);
assert!(dev.write_at(2, &[1, 2, 3, 4]).is_err());
assert_eq!(dev.size_bytes(), 4, "the device did not grow");
}
#[test]
fn reads_and_writes_inside_the_buffer_round_trip() {
let dev = RwBytes::new(vec![0u8; 16]);
dev.write_at(4, &[1, 2, 3, 4]).unwrap();
let mut buf = [0u8; 4];
dev.read_at(4, &mut buf).unwrap();
assert_eq!(buf, [1, 2, 3, 4]);
}
}