use crate::block::{BlockDevice, BlockRead};
use crate::error::{Error, Result};
use std::sync::Arc;
pub struct SliceReader<'a> {
parent: &'a (dyn BlockRead + 'a),
start: u64,
length: u64,
}
impl<'a> SliceReader<'a> {
pub fn new(parent: &'a (dyn BlockRead + 'a), start: u64, length: u64) -> Self {
Self {
parent,
start,
length,
}
}
pub fn start(&self) -> u64 {
self.start
}
pub fn length(&self) -> u64 {
self.length
}
}
impl<'a> BlockRead for SliceReader<'a> {
fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<()> {
let want = buf.len() as u64;
if offset
.checked_add(want)
.map(|e| e > self.length)
.unwrap_or(true)
{
return Err(Error::ShortRead {
offset,
want: buf.len(),
got: 0,
});
}
self.parent.read_at(self.start + offset, buf)
}
fn size_bytes(&self) -> u64 {
self.length
}
}
impl<'a> BlockDevice for SliceReader<'a> {}
pub struct OwnedSlice {
parent: Arc<dyn BlockRead>,
start: u64,
length: u64,
}
impl OwnedSlice {
pub fn new(parent: Arc<dyn BlockRead>, start: u64, length: u64) -> Self {
Self {
parent,
start,
length,
}
}
pub fn start(&self) -> u64 {
self.start
}
pub fn length(&self) -> u64 {
self.length
}
}
impl BlockRead for OwnedSlice {
fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<()> {
let want = buf.len() as u64;
if offset
.checked_add(want)
.map(|e| e > self.length)
.unwrap_or(true)
{
return Err(Error::ShortRead {
offset,
want: buf.len(),
got: 0,
});
}
self.parent.read_at(self.start + offset, buf)
}
fn size_bytes(&self) -> u64 {
self.length
}
}
impl BlockDevice for OwnedSlice {}
pub struct OwnedRwSlice {
parent: Arc<dyn BlockDevice>,
start: u64,
length: u64,
}
impl OwnedRwSlice {
pub fn new(parent: Arc<dyn BlockDevice>, start: u64, length: u64) -> Self {
Self {
parent,
start,
length,
}
}
pub fn start(&self) -> u64 {
self.start
}
pub fn length(&self) -> u64 {
self.length
}
}
impl BlockRead for OwnedRwSlice {
fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<()> {
let want = buf.len() as u64;
if offset
.checked_add(want)
.map(|e| e > self.length)
.unwrap_or(true)
{
return Err(Error::ShortRead {
offset,
want: buf.len(),
got: 0,
});
}
self.parent.read_at(self.start + offset, buf)
}
fn size_bytes(&self) -> u64 {
self.length
}
}
impl BlockDevice for OwnedRwSlice {
fn write_at(&self, offset: u64, buf: &[u8]) -> Result<()> {
let want = buf.len() as u64;
if offset
.checked_add(want)
.map(|e| e > self.length)
.unwrap_or(true)
{
return Err(Error::OutOfBounds {
offset,
len: want,
size: self.length,
});
}
if !self.parent.is_writable() {
return Err(Error::ReadOnly);
}
self.parent.write_at(self.start + offset, buf)
}
fn flush(&self) -> Result<()> {
self.parent.flush()
}
fn is_writable(&self) -> bool {
self.parent.is_writable()
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::Mutex;
struct Bytes(Mutex<Vec<u8>>);
impl BlockRead for Bytes {
fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<()> {
let 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),
});
}
buf.copy_from_slice(&b[start..end]);
Ok(())
}
fn size_bytes(&self) -> u64 {
self.0.lock().unwrap().len() as u64
}
}
#[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 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);
}
struct RwBytes(Mutex<Vec<u8>>);
impl BlockRead for RwBytes {
fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<()> {
let 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),
});
}
buf.copy_from_slice(&b[start..end]);
Ok(())
}
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 s = offset as usize;
b[s..s + buf.len()].copy_from_slice(buf);
Ok(())
}
fn is_writable(&self) -> bool {
true
}
}
#[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 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();
}
}