use crate::block::{BlockDevice, BlockRead};
use crate::error::{Error, Result};
use std::sync::Arc;
pub fn window_on_parent(parent_size: u64, start: u64, length: u64) -> Option<u64> {
if start >= parent_size {
return None;
}
Some(length.min(parent_size - start))
}
#[derive(Clone, Copy)]
struct SliceGeometry {
start: u64,
length: u64,
}
impl SliceGeometry {
fn new(parent_size: u64, start: u64, length: u64) -> Self {
Self {
start,
length: window_on_parent(parent_size, start, length).unwrap_or(0),
}
}
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 {
let geom = SliceGeometry::new(parent.size_bytes(), start, length);
Self { parent, geom }
}
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 {
let geom = SliceGeometry::new(parent.size_bytes(), start, length);
Self { parent, geom }
}
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 {
let geom = SliceGeometry::new(parent.size_bytes(), start, length);
Self { parent, geom }
}
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);
assert_eq!(
slice.size_bytes(),
0,
"the window begins past the parent, so none of it is there"
);
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 window_on_parent_clamps_the_length_and_refuses_only_a_start_past_the_end() {
assert_eq!(window_on_parent(1024, 0, 1024), Some(1024));
assert_eq!(window_on_parent(1024, 512, 512), Some(512));
assert_eq!(window_on_parent(1024, 1023, 1), Some(1));
assert_eq!(window_on_parent(1024, 512, 513), Some(512));
assert_eq!(window_on_parent(1024, 0, u64::MAX), Some(1024));
assert_eq!(window_on_parent(1024, 1024, 1), None);
assert_eq!(window_on_parent(1024, 4096, 1), None);
assert_eq!(window_on_parent(0, 0, 8), None);
assert_eq!(
window_on_parent(64 * 1024, 1 << 63, (1 << 63) + 51200),
None
);
assert_eq!(window_on_parent(u64::MAX, u64::MAX - 4, 8), Some(4));
}
#[test]
fn a_borrowed_slice_cannot_claim_more_than_its_parent_holds() {
let dev = Bytes::new(vec![0xEE; 100]);
let slice = SliceReader::new(&dev, 50, 100);
assert_eq!(slice.size_bytes(), 50);
assert_eq!(slice.length(), 50);
assert_eq!(
slice.start(),
50,
"the start is not clamped, only the length"
);
let mut buf = [0u8; 8];
match slice.read_at(45, &mut buf) {
Err(Error::ShortRead { offset, want, got }) => {
assert_eq!((offset, want, got), (45, 8, 0));
}
other => panic!("expected the slice's own ShortRead, got {other:?}"),
}
assert_eq!(buf, [0u8; 8], "a refused read leaves the buffer alone");
}
#[test]
fn an_owned_slice_cannot_claim_more_than_its_parent_holds() {
let mut v = vec![0u8; 100];
v[50..58].copy_from_slice(b"LASTHALF");
let dev: Arc<dyn BlockRead> = Arc::new(Bytes::new(v));
let slice = OwnedSlice::new(dev, 50, 100);
assert_eq!(slice.size_bytes(), 50);
assert_eq!(slice.length(), 50);
let mut buf = [0u8; 8];
slice.read_at(0, &mut buf).unwrap();
assert_eq!(&buf, b"LASTHALF");
match slice.read_at(45, &mut buf) {
Err(Error::ShortRead { offset, want, got }) => {
assert_eq!((offset, want, got), (45, 8, 0));
}
other => panic!("expected the slice's own ShortRead, got {other:?}"),
}
}
#[test]
fn an_rw_slice_refuses_a_write_past_its_parents_end_as_out_of_bounds() {
let dev: Arc<dyn BlockDevice> = Arc::new(RwBytes::new(vec![0u8; 64]));
let slice = OwnedRwSlice::new(dev.clone(), 32, 4096);
assert_eq!(slice.size_bytes(), 32);
slice.write_at(0, &[0xAB; 4]).unwrap();
let mut pbuf = [0u8; 4];
dev.read_at(32, &mut pbuf).unwrap();
assert_eq!(pbuf, [0xAB; 4]);
match slice.write_at(32, &[0xCD; 8]) {
Err(Error::OutOfBounds { offset, len, size }) => {
assert_eq!((offset, len, size), (32, 8, 32));
}
other => panic!("expected the slice's own OutOfBounds, got {other:?}"),
}
assert_eq!(dev.size_bytes(), 64, "the parent did not grow");
}
#[test]
fn a_slice_starting_past_its_parents_end_is_empty() {
let mut v = vec![0u8; 100];
v[0..8].copy_from_slice(b"NOTYOURS");
let dev: Arc<dyn BlockRead> = Arc::new(Bytes::new(v));
let slice = OwnedSlice::new(dev, 200, 64);
assert_eq!(slice.size_bytes(), 0);
let mut buf = [0u8; 8];
match slice.read_at(0, &mut buf) {
Err(Error::ShortRead { offset, want, got }) => {
assert_eq!((offset, want, got), (0, 8, 0));
}
other => panic!("expected ShortRead, got {other:?}"),
}
assert_eq!(buf, [0u8; 8]);
}
#[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();
}
}