use crate::block::{BlockDevice, BlockRead};
use crate::error::{Error, Result};
use std::sync::Arc;
#[derive(Clone, Copy)]
struct SliceGeometry {
start: u64,
length: u64,
}
impl SliceGeometry {
fn new(start: u64, length: u64) -> Self {
Self { start, length }
}
fn rebase(&self, offset: u64, len: u64) -> Option<u64> {
let end = offset.checked_add(len)?;
if end > self.length {
return None;
}
self.start.checked_add(offset)
}
fn rebase_read(&self, offset: u64, len: usize) -> Result<u64> {
self.rebase(offset, len as u64).ok_or(Error::ShortRead {
offset,
want: len,
got: 0,
})
}
fn rebase_write(&self, offset: u64, len: usize) -> Result<u64> {
self.rebase(offset, len as u64).ok_or(Error::OutOfBounds {
offset,
len: len as u64,
size: self.length,
})
}
}
pub struct SliceReader<'a> {
parent: &'a (dyn BlockRead + 'a),
geom: SliceGeometry,
}
impl<'a> SliceReader<'a> {
pub fn new(parent: &'a (dyn BlockRead + 'a), start: u64, length: u64) -> Self {
Self {
parent,
geom: SliceGeometry::new(start, length),
}
}
pub fn start(&self) -> u64 {
self.geom.start
}
pub fn length(&self) -> u64 {
self.geom.length
}
}
impl<'a> BlockRead for SliceReader<'a> {
fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<()> {
let at = self.geom.rebase_read(offset, buf.len())?;
self.parent.read_at(at, buf)
}
fn size_bytes(&self) -> u64 {
self.geom.length
}
}
impl<'a> BlockDevice for SliceReader<'a> {}
pub struct OwnedSlice {
parent: Arc<dyn BlockRead>,
geom: SliceGeometry,
}
impl OwnedSlice {
pub fn new(parent: Arc<dyn BlockRead>, start: u64, length: u64) -> Self {
Self {
parent,
geom: SliceGeometry::new(start, length),
}
}
pub fn start(&self) -> u64 {
self.geom.start
}
pub fn length(&self) -> u64 {
self.geom.length
}
}
impl BlockRead for OwnedSlice {
fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<()> {
let at = self.geom.rebase_read(offset, buf.len())?;
self.parent.read_at(at, buf)
}
fn size_bytes(&self) -> u64 {
self.geom.length
}
}
impl BlockDevice for OwnedSlice {}
pub struct OwnedRwSlice {
parent: Arc<dyn BlockDevice>,
geom: SliceGeometry,
}
impl OwnedRwSlice {
pub fn new(parent: Arc<dyn BlockDevice>, start: u64, length: u64) -> Self {
Self {
parent,
geom: SliceGeometry::new(start, length),
}
}
pub fn start(&self) -> u64 {
self.geom.start
}
pub fn length(&self) -> u64 {
self.geom.length
}
}
impl BlockRead for OwnedRwSlice {
fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<()> {
let at = self.geom.rebase_read(offset, buf.len())?;
self.parent.read_at(at, buf)
}
fn size_bytes(&self) -> u64 {
self.geom.length
}
}
impl BlockDevice for OwnedRwSlice {
fn write_at(&self, offset: u64, buf: &[u8]) -> Result<()> {
let at = self.geom.rebase_write(offset, buf.len())?;
if !self.parent.is_writable() {
return Err(Error::ReadOnly);
}
self.parent.write_at(at, buf)
}
fn flush(&self) -> Result<()> {
self.parent.flush()
}
fn is_writable(&self) -> bool {
self.parent.is_writable()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_device::{Bytes, RwBytes};
use std::sync::Mutex;
#[test]
fn slice_reader_rebases_offsets() {
let mut v = vec![0u8; 4096];
v[2000..2004].copy_from_slice(&[0xAB, 0xCD, 0xEF, 0x01]);
let dev = Bytes(Mutex::new(v));
let slice = SliceReader::new(&dev, 2000, 4);
assert_eq!(slice.size_bytes(), 4);
assert_eq!(slice.start(), 2000);
assert_eq!(slice.length(), 4);
let mut buf = [0u8; 4];
slice.read_at(0, &mut buf).unwrap();
assert_eq!(buf, [0xAB, 0xCD, 0xEF, 0x01]);
}
#[test]
fn a_slice_whose_start_plus_offset_leaves_the_parent_reads_nothing() {
let mut v = vec![0u8; 64 * 1024];
v[5000..5008].copy_from_slice(b"SECRET!!");
let dev: Arc<dyn BlockRead> = Arc::new(Bytes(Mutex::new(v)));
let slice = OwnedSlice::new(dev, 1 << 63, (1 << 63) + 51200);
let mut buf = [0u8; 8];
let inside_the_declared_length = (1u64 << 63) + 5000;
let outcome = slice.read_at(inside_the_declared_length, &mut buf);
assert!(
outcome.is_err(),
"the read succeeded and returned {:?}, which is the parent's \
bytes from offset 5000",
std::str::from_utf8(&buf)
);
assert_ne!(&buf, b"SECRET!!");
}
#[test]
fn slice_reader_rejects_out_of_bounds() {
let dev = Bytes(Mutex::new(vec![0u8; 4096]));
let slice = SliceReader::new(&dev, 0, 16);
let mut buf = [0u8; 8];
match slice.read_at(12, &mut buf) {
Err(Error::ShortRead { .. }) => {}
other => panic!("expected ShortRead, got {other:?}"),
}
}
#[test]
fn owned_slice_works_through_arc() {
let mut v = vec![0u8; 4096];
v[100..104].copy_from_slice(&[0x11, 0x22, 0x33, 0x44]);
let dev: Arc<dyn BlockRead> = Arc::new(Bytes(Mutex::new(v)));
let slice = OwnedSlice::new(dev, 100, 4);
assert_eq!(slice.size_bytes(), 4);
let mut buf = [0u8; 4];
slice.read_at(0, &mut buf).unwrap();
assert_eq!(buf, [0x11, 0x22, 0x33, 0x44]);
}
#[test]
fn slices_reject_writes_via_blockdevice_default() {
let dev = Bytes(Mutex::new(vec![0u8; 16]));
let slice = SliceReader::new(&dev, 0, 8);
let err = BlockDevice::write_at(&slice, 0, &[1u8; 4]).unwrap_err();
assert!(matches!(err, Error::ReadOnly));
}
#[test]
fn owned_slice_accessors_report_geometry() {
let dev: Arc<dyn BlockRead> = Arc::new(Bytes(Mutex::new(vec![0u8; 4096])));
let slice = OwnedSlice::new(dev, 512, 256);
assert_eq!(slice.start(), 512);
assert_eq!(slice.length(), 256);
assert_eq!(slice.size_bytes(), 256);
}
#[test]
fn owned_rw_slice_accessors_report_geometry() {
let dev: Arc<dyn BlockDevice> = Arc::new(RwBytes(Mutex::new(vec![0u8; 64])));
let slice = OwnedRwSlice::new(dev, 16, 32);
assert_eq!(slice.start(), 16);
assert_eq!(slice.length(), 32);
assert_eq!(slice.size_bytes(), 32);
assert!(slice.is_writable());
}
#[test]
fn owned_rw_slice_rebases_reads_and_writes() {
let dev: Arc<dyn BlockDevice> = Arc::new(RwBytes(Mutex::new(vec![0u8; 64])));
let slice = OwnedRwSlice::new(dev.clone(), 16, 32);
slice.write_at(0, &[0xDE, 0xAD, 0xBE, 0xEF]).unwrap();
let mut buf = [0u8; 4];
slice.read_at(0, &mut buf).unwrap();
assert_eq!(buf, [0xDE, 0xAD, 0xBE, 0xEF]);
let mut pbuf = [0u8; 4];
dev.read_at(16, &mut pbuf).unwrap();
assert_eq!(pbuf, [0xDE, 0xAD, 0xBE, 0xEF]);
}
#[test]
fn owned_rw_slice_rejects_out_of_bounds_write() {
let dev: Arc<dyn BlockDevice> = Arc::new(RwBytes(Mutex::new(vec![0u8; 64])));
let slice = OwnedRwSlice::new(dev, 0, 8);
match slice.write_at(6, &[0u8; 4]) {
Err(Error::OutOfBounds { .. }) => {}
other => panic!("expected OutOfBounds, got {other:?}"),
}
}
#[test]
fn same_out_of_range_span_is_short_read_for_a_read_and_out_of_bounds_for_a_write() {
let dev: Arc<dyn BlockDevice> = Arc::new(RwBytes(Mutex::new(vec![0u8; 64])));
let slice = OwnedRwSlice::new(dev, 16, 8);
let mut buf = [0u8; 4];
match slice.read_at(6, &mut buf) {
Err(Error::ShortRead { offset, want, got }) => {
assert_eq!((offset, want, got), (6, 4, 0));
}
other => panic!("expected ShortRead, got {other:?}"),
}
match slice.write_at(6, &[0u8; 4]) {
Err(Error::OutOfBounds { offset, len, size }) => {
assert_eq!((offset, len, size), (6, 4, 8));
}
other => panic!("expected OutOfBounds, got {other:?}"),
}
}
#[test]
fn owned_rw_slice_flush_delegates_to_parent() {
let dev: Arc<dyn BlockDevice> = Arc::new(RwBytes(Mutex::new(vec![0u8; 8])));
let slice = OwnedRwSlice::new(dev, 0, 8);
slice.flush().unwrap();
}
}