use crate::BlockIo;
use core::fmt::{self, Debug, Display, Formatter};
use core::ops::Range;
use gpt_disk_types::{BlockSize, Lba};
#[allow(clippy::module_name_repetitions)]
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub enum SliceBlockIoError {
Overflow,
ReadOnly,
OutOfBounds {
start_lba: Lba,
length_in_bytes: usize,
},
}
impl Default for SliceBlockIoError {
fn default() -> Self {
SliceBlockIoError::Overflow
}
}
impl Display for SliceBlockIoError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Self::Overflow => f.write_str("numeric overflow occurred"),
Self::ReadOnly => {
f.write_str("attempted to write to a read-only byte slice")
}
Self::OutOfBounds {
start_lba,
length_in_bytes,
} => {
write!(
f,
"out of bounds: start_lba={}, length_in_bytes={}",
start_lba, length_in_bytes
)
}
}
}
}
fn buffer_byte_range_opt(
block_size: BlockSize,
start_lba: Lba,
buf: &[u8],
) -> Option<Range<usize>> {
let start_lba = usize::try_from(start_lba).ok()?;
let start_byte = start_lba.checked_mul(block_size.to_usize()?)?;
let end_byte = start_byte.checked_add(buf.len())?;
Some(start_byte..end_byte)
}
fn buffer_byte_range(
block_size: BlockSize,
start_lba: Lba,
buf: &[u8],
) -> Result<Range<usize>, SliceBlockIoError> {
buffer_byte_range_opt(block_size, start_lba, buf)
.ok_or(SliceBlockIoError::Overflow)
}
#[allow(clippy::module_name_repetitions)]
pub struct SliceBlockIo<'a> {
data: &'a [u8],
block_size: BlockSize,
}
impl<'a> SliceBlockIo<'a> {
#[must_use]
pub fn new(data: &'a [u8], block_size: BlockSize) -> Self {
Self { data, block_size }
}
}
impl<'a> BlockIo for SliceBlockIo<'a> {
type Error = SliceBlockIoError;
fn block_size(&self) -> BlockSize {
self.block_size
}
fn num_blocks(&mut self) -> Result<u64, Self::Error> {
let data_len = u64::try_from(self.data.len())
.map_err(|_| SliceBlockIoError::Overflow)?;
Ok(data_len / self.block_size().to_u64())
}
fn read_blocks(
&mut self,
start_lba: Lba,
dst: &mut [u8],
) -> Result<(), Self::Error> {
self.assert_valid_buffer(dst);
let src = self
.data
.get(buffer_byte_range(self.block_size(), start_lba, dst)?)
.ok_or(Self::Error::OutOfBounds {
start_lba,
length_in_bytes: dst.len(),
})?;
dst.copy_from_slice(src);
Ok(())
}
fn write_blocks(
&mut self,
_start_lba: Lba,
_src: &[u8],
) -> Result<(), Self::Error> {
Err(Self::Error::ReadOnly)
}
fn flush(&mut self) -> Result<(), Self::Error> {
Ok(())
}
}
#[allow(clippy::module_name_repetitions)]
pub struct MutSliceBlockIo<'a> {
data: &'a mut [u8],
block_size: BlockSize,
}
impl<'a> MutSliceBlockIo<'a> {
pub fn new(data: &'a mut [u8], block_size: BlockSize) -> Self {
Self { data, block_size }
}
fn buffer_byte_range_opt(
&self,
start_lba: Lba,
buf: &[u8],
) -> Option<Range<usize>> {
let start_lba = usize::try_from(start_lba).ok()?;
let start_byte =
start_lba.checked_mul(self.block_size().to_usize()?)?;
let end_byte = start_byte.checked_add(buf.len())?;
Some(start_byte..end_byte)
}
fn buffer_byte_range(
&self,
start_lba: Lba,
buf: &[u8],
) -> Result<Range<usize>, SliceBlockIoError> {
self.buffer_byte_range_opt(start_lba, buf)
.ok_or(SliceBlockIoError::Overflow)
}
}
impl<'a> BlockIo for MutSliceBlockIo<'a> {
type Error = SliceBlockIoError;
fn block_size(&self) -> BlockSize {
self.block_size
}
fn num_blocks(&mut self) -> Result<u64, Self::Error> {
let data_len = u64::try_from(self.data.len())
.map_err(|_| SliceBlockIoError::Overflow)?;
Ok(data_len / self.block_size().to_u64())
}
fn read_blocks(
&mut self,
start_lba: Lba,
dst: &mut [u8],
) -> Result<(), Self::Error> {
self.assert_valid_buffer(dst);
let src = self
.data
.get(self.buffer_byte_range(start_lba, dst)?)
.ok_or(Self::Error::OutOfBounds {
start_lba,
length_in_bytes: dst.len(),
})?;
dst.copy_from_slice(src);
Ok(())
}
fn write_blocks(
&mut self,
start_lba: Lba,
src: &[u8],
) -> Result<(), Self::Error> {
self.assert_valid_buffer(src);
let dst = self
.data
.get_mut(self.buffer_byte_range(start_lba, src)?)
.ok_or(Self::Error::OutOfBounds {
start_lba,
length_in_bytes: src.len(),
})?;
dst.copy_from_slice(src);
Ok(())
}
fn flush(&mut self) -> Result<(), Self::Error> {
Ok(())
}
}