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, end) = range_within(b.len(), offset, buf.len())?;
b[start..end].copy_from_slice(buf);
Ok(())
}
fn is_writable(&self) -> bool {
true
}
}
pub(crate) fn range_within(len: usize, offset: u64, want: usize) -> Result<(usize, usize)> {
let len64 = len as u64;
let short = || Error::ShortRead {
offset,
want,
got: 0,
};
let end = offset.checked_add(want as u64).ok_or_else(short)?;
if end > len64 {
return Err(short());
}
Ok((offset as usize, end as usize))
}
fn read_into(b: &[u8], offset: u64, buf: &mut [u8]) -> Result<()> {
let (start, end) = range_within(b.len(), offset, buf.len())?;
buf.copy_from_slice(&b[start..end]);
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_read_at_a_wild_offset_is_a_short_read_not_a_panic() {
let dev = Bytes::new(vec![0xAB; 8]);
for offset in [u64::MAX, u64::MAX - 7, 1 << 40, 9] {
let mut buf = [0u8; 8];
match dev.read_at(offset, &mut buf).expect_err("past the end") {
Error::ShortRead {
offset: o,
want,
got,
} => {
assert_eq!((o, want), (offset, 8));
assert_eq!(got, 0, "nothing is available at {offset}");
}
other => panic!("expected ShortRead at {offset}, got {other:?}"),
}
assert_eq!(buf, [0u8; 8], "a refused read leaves the buffer alone");
}
}
#[test]
fn a_write_at_a_wild_offset_is_refused_rather_than_panicking() {
let dev = RwBytes::new(vec![0u8; 8]);
for offset in [u64::MAX, u64::MAX - 7, 1 << 40, 9] {
match dev.write_at(offset, &[1u8; 8]).expect_err("past the end") {
Error::ShortRead {
offset: o, want, ..
} => {
assert_eq!((o, want), (offset, 8))
}
other => panic!("expected ShortRead at {offset}, got {other:?}"),
}
}
let mut buf = [0u8; 8];
dev.read_at(0, &mut buf).unwrap();
assert_eq!(buf, [0u8; 8], "no refused write touched the buffer");
}
#[test]
fn a_length_that_overflows_when_added_to_the_offset_is_refused() {
let dev = Bytes::new(vec![0xAB; 8]);
let mut buf = vec![0u8; 16];
let offset = (usize::MAX - 8) as u64;
match dev.read_at(offset, &mut buf).expect_err("cannot fit") {
Error::ShortRead { want, got, .. } => {
assert_eq!(want, 16);
assert_eq!(got, 0);
}
other => panic!("expected ShortRead, got {other:?}"),
}
}
#[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, 0)),
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]);
}
}