Files
RedBear-OS/src/out.rs
T
Jacob Lorentzon 9c5f11fbc8 Add Out<T> wrapper
2025-09-21 14:36:12 +02:00

168 lines
5.5 KiB
Rust

//! Wrapper for the "out pointer" pattern.
//!
//! This is functionally equivalent to `&Cell<MaybeUninit<T>>` except the only allowed operation is
//! to write a `T`. Using `MaybeUninit` directly would not have been equally general; a
//! `&mut MaybeUninit<T>` could never then be created from a `&mut T` and passed to safe code,
//! which can safely replace it with `MaybeUninit::uninit` and make the existence of `&mut T` UB.
//!
//! As for the "`&Cell<...>`", this is to be slightly weaker than Rust's normally strict
//! requirement that `&mut` references are never aliased, which can typically not be assumed when
//! getting pointers from C.
use core::{cell::UnsafeCell, fmt, marker::PhantomData, mem::MaybeUninit, ptr::NonNull};
/// Wrapper for write-only "out pointers" that are safe to write to
// TODO: We may want to change this to &mut MaybeUninit, or have a generic parameter deciding
// whether it should be noalias or not
#[derive(Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Out<'a, T: ?Sized> {
ptr: NonNull<T>,
_marker: PhantomData<&'a UnsafeCell<T>>,
}
impl<'a, T: ?Sized> Out<'a, T> {
/// # Safety
///
/// - pointer must either be NULL, or be valid for the duration of lifetime `'a`
#[inline]
pub unsafe fn nullable(ptr: *mut T) -> Option<Self> {
Some(Self {
ptr: NonNull::new(ptr)?,
_marker: PhantomData,
})
}
/// # Safety
///
/// - pointer must be valid for the duration of lifetime `'a`
#[inline]
pub unsafe fn nonnull(ptr: *mut T) -> Self {
if cfg!(debug_assertions) {
assert!(!ptr.is_null());
}
Self {
ptr: NonNull::new_unchecked(ptr),
_marker: PhantomData,
}
}
#[inline]
pub fn from_mut(r: &'a mut T) -> Self {
// SAFETY:
//
// - `r` will obviously have the same lifetime as Self
// - a Rust reference is obviously valid as a pointer, and the lifetime is tied to that of
// this struct
unsafe { Self::nonnull(r) }
}
#[inline]
pub fn as_mut_ptr(&mut self) -> *mut T {
self.ptr.as_ptr()
}
}
impl<'a, T> Out<'a, T> {
#[inline]
pub fn from_uninit_mut(r: &'a mut MaybeUninit<T>) -> Self {
// SAFETY:
//
// Same as for from_mut. It's fine if *r is uninitialized, as this wrapper only allows
// writes.
unsafe { Self::nonnull(r.as_mut_ptr()) }
}
#[inline]
pub fn write(&mut self, t: T) {
unsafe {
self.ptr.as_ptr().write(t);
}
}
}
impl<'a, T> Out<'a, [T]> {
/// # Safety
///
/// If `len > 0`, `ptr` be valid for `len` elements of `T`, during lifetime `'a`.
pub unsafe fn from_raw_parts(ptr: *mut T, len: usize) -> Self {
// Empty slices must be non-NULL in Rust, but C typically does not force this for
// pointer-length pairs.
let ptr = if len == 0 {
core::ptr::dangling_mut::<T>()
} else {
ptr
};
Self::nonnull(core::slice::from_raw_parts_mut(ptr, len))
}
pub fn len(&self) -> usize {
self.ptr.as_ptr().len()
}
// TODO: Maybe strengthen lifetimes?
#[inline]
pub fn split_at_checked<'b>(&'b mut self, n: usize) -> Option<[Out<'b, [T]>; 2]> {
let l = self.ptr.len();
if n > l {
return None;
}
Some([
Out {
ptr: unsafe {
NonNull::new_unchecked(core::ptr::slice_from_raw_parts_mut(
self.ptr.as_mut_ptr(),
n,
))
},
_marker: PhantomData,
},
Out {
ptr: unsafe {
NonNull::new_unchecked(core::ptr::slice_from_raw_parts_mut(
self.ptr.as_mut_ptr().add(n),
l - n,
))
},
_marker: PhantomData,
},
])
}
#[inline]
pub fn copy_from_slice(&mut self, src: &[T])
where
T: Copy,
{
assert_eq!(
self.ptr.len(),
src.len(),
"Out::copy_from_slice size mismatch"
);
unsafe {
// SAFETY:
//
// - we have already know from the existence of self that the slice is a valid writable
// pointer
// - src is similarly also a valid readable pointer of the same type
// - because of `T: Copy`, it is valid to copy bytes directly
// - although self.ptr may alias, src must not alias with any writable pointer, and the
// Copy bound ensures T cannot have interior mutability since `UnsafeCell: !Copy`
self.ptr
.as_mut_ptr()
.copy_from_nonoverlapping(src.as_ptr(), src.len());
}
}
}
// TODO: different trait?
impl<T: plain::Plain> Out<'_, [T]> {
pub fn zero(&mut self) {
let l = self.ptr.len();
unsafe {
// SAFETY:
// - already know the pointer is valid up to its length
// - the Plain trait ensures zero is a valid bit pattern
self.ptr.as_mut_ptr().write_bytes(0, l)
}
}
}
impl<T: ?Sized> fmt::Pointer for Out<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:p}", self.ptr)
}
}
impl<T: ?Sized> fmt::Debug for Out<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "[Out: {:p}]", self.ptr)
}
}