use crate::io::ScheduledSource;
use std::{num::NonZeroUsize, ptr::NonNull};
#[derive(Default, 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: ScheduledSource,
mem: NonNull<u8>,
len: NonZeroUsize,
}
impl core::ops::Deref for ReadResultInner {
type Target = [u8];
fn deref(&self) -> &[u8] {
unsafe { std::slice::from_raw_parts(self.mem.as_ptr(), self.len.get()) }
}
}
impl ReadResult {
pub(crate) fn empty_buffer() -> Self {
Self(None)
}
pub(crate) fn from_sliced_buffer(buffer: ScheduledSource, offset: usize, len: usize) -> Self {
Self(
NonZeroUsize::new(len)
.map(|len| unsafe {
NonNull::new(buffer.as_bytes().as_ptr() as *mut u8).map(|ptr| {
let mem = NonNull::new_unchecked(ptr.as_ptr().add(offset));
let ret = ReadResultInner { buffer, mem, len };
ReadResultInner::check_invariants(&ret);
ret
})
})
.unwrap_or(None),
)
}
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) {
unsafe {
debug_assert!(
this.mem.as_ptr().add(this.len.get() - 1) as *const u8
<= this.buffer.as_bytes().last().unwrap() as *const u8,
"a ReadResult contains an out-of-range 'end': offset ({:?} + {}) > {:?} buffer \
length ({})",
this.mem,
this.len,
this.buffer.as_bytes().last().unwrap() as *const u8,
this.buffer.as_bytes().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 {
unsafe {
Some(ReadResultInner {
buffer: this.buffer.clone(),
mem: NonNull::new_unchecked(this.mem.as_ptr().add(extra_offset)),
len,
})
}
}
}
unsafe fn slice_unchecked(this: &Self, extra_offset: usize, len: NonZeroUsize) -> Self {
Self::check_invariants(this);
debug_assert!(
extra_offset <= this.len.get(),
"offset {} is more than the length ({}) of the slice",
extra_offset,
this.len,
);
debug_assert!(
len.get() <= (this.len.get() - extra_offset),
"length {} would cross past the end ({}) of the slice",
len.get() + extra_offset,
this.len,
);
ReadResultInner {
buffer: this.buffer.clone(),
mem: NonNull::new_unchecked(this.mem.as_ptr().add(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>());
}
}