use crate::sys::DmaBuffer;
use core::num::NonZeroUsize;
use std::rc::Rc;
#[derive(Clone, Debug)]
pub struct ReadResult(Option<ReadResultInner>);
impl core::ops::Deref for ReadResult {
type Target = [u8];
fn deref(&self) -> &[u8] {
self.0.as_deref().unwrap_or(&[])
}
}
#[derive(Clone, Debug)]
struct ReadResultInner {
buffer: Rc<DmaBuffer>,
offset: usize,
len: NonZeroUsize,
}
impl core::ops::Deref for ReadResultInner {
type Target = [u8];
fn deref(&self) -> &[u8] {
&self.buffer.as_bytes()[self.offset..][..self.len.get()]
}
}
impl ReadResult {
pub(crate) fn empty_buffer() -> Self {
Self(None)
}
pub(crate) fn from_whole_buffer(buffer: DmaBuffer) -> Self {
Self(NonZeroUsize::new(buffer.len()).map(|len| ReadResultInner {
buffer: Rc::new(buffer),
offset: 0,
len,
}))
}
pub fn slice(this: &Self, extra_offset: usize, len: usize) -> Option<Self> {
Some(Self(if let Some(len) = NonZeroUsize::new(len) {
Some(ReadResultInner::slice(this.0.as_ref()?, extra_offset, len)?)
} else {
None
}))
}
pub unsafe fn slice_unchecked(this: &Self, extra_offset: usize, len: usize) -> Self {
Self(NonZeroUsize::new(len).map(|len| {
ReadResultInner::slice_unchecked(this.0.as_ref().unwrap(), extra_offset, len)
}))
}
}
impl ReadResultInner {
fn check_invariants(this: &Self) {
if cfg!(debug_assertions) {
let max_len = this.buffer.len();
assert!(
(this.offset + this.len.get()) <= max_len,
"a ReadResult contains an out-of-range 'end': offset ({} + {}) > buffer length ({})",
this.offset,
this.len,
max_len,
);
}
}
fn slice(this: &Self, extra_offset: usize, len: NonZeroUsize) -> Option<Self> {
Self::check_invariants(this);
if extra_offset > this.len.get() || len.get() > (this.len.get() - extra_offset) {
None
} else {
Some(ReadResultInner {
buffer: this.buffer.clone(),
offset: this.offset + extra_offset,
len,
})
}
}
unsafe fn slice_unchecked(this: &Self, extra_offset: usize, len: NonZeroUsize) -> Self {
Self::check_invariants(this);
if cfg!(debug_assertions) {
assert!(
extra_offset <= this.len.get(),
"offset {} is more than the length ({}) of the slice",
extra_offset,
this.len,
);
assert!(
len.get() <= (this.len.get() - extra_offset),
"length {} would cross past the end ({}) of the slice",
len.get() + extra_offset,
this.len,
);
}
Self {
buffer: this.buffer.clone(),
offset: this.offset + extra_offset,
len,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn equal_struct_size() {
use core::mem::size_of;
assert_eq!(size_of::<ReadResult>(), size_of::<ReadResultInner>());
}
}