uninit_tools/
lib.rs

1//!
2//! # Examples
3//!
4//! ## A `Read`-like trait implemented to better support uninitialized memory.
5//! ```
6//! # use std::io;
7//!
8//! use std::mem::MaybeUninit;
9//!
10//! use uninit_tools::buffer::{Buffer, BufferRef};
11//! use uninit_tools::traits::Initialize;
12//! # // TODO: Add more safe abstractions for slices of I/O slices.
13//!
14//! pub trait MyRead {
15//!     // NOTE: The function does not return any count, since the buffer keeps track of that.
16//!     //
17//!     // Rather than using `&mut Buffer<T>` directly, we use `BufferRef<'_, T>` to prevent the
18//!     // caller from replacing the buffer that is being filled with something different. It also
19//!     // gives the `Read` implementor a reduced subset of the functionality, to that it cannot
20//!     // for example read the bytes that are already written into the buffer.
21//!     fn read<'buffer, T>(&mut self, buffer: BufferRef<'buffer, T>) -> io::Result<()>
22//!     where
23//!         T: Initialize<Item = u8>,
24//!     ;
25//! }
26//!
27//! impl MyRead for &[u8] {
28//!     fn read<'buffer, T>(&mut self, mut buffer: BufferRef<'buffer, T>) -> io::Result<()>
29//!     where
30//!         T: Initialize<Item = u8>,
31//!     {
32//!         // Get the minimum number of bytes to copy. Note that it will panic if the source slice
33//!         // were to overflow, as with the regular `copy_from_slice` function for regular slices.
34//!         let min = std::cmp::min(self.len(), buffer.remaining());
35//!
36//!         // Advance the buffer by simply copying the source slice.
37//!         buffer.append(&self[..min]);
38//!
39//!         Ok(())
40//!     }
41//! }
42//!
43//! # fn main() -> io::Result<()> {
44//!
45//! // NOTE: The `Initialize` trait is implemented for arrays of all sizes, thanks to const
46//! // generics.
47//! let array = [MaybeUninit::uninit(); 32];
48//! let len = array.len();
49//! let mut buf = Buffer::uninit(array);
50//!
51//! let original_stupid_text: &[u8] = b"copying is expensive!";
52//! let mut stupid_text = original_stupid_text;
53//!
54//! // Read as many bytes as possible.
55//! stupid_text.read(buf.by_ref())?;
56//!
57//! // Note that while we cannot do anything useful with the rest of the buffer, we can still use
58//! // it as the destination of even more I/O, or simply check its length like we do here.
59//! assert_eq!(buf.remaining(), len - original_stupid_text.len());
60//!
61//! # Ok(())
62//!
63//! # }
64//!
65//! ```
66//!
67//! Note that this may not be the best implementation of the `Read` trait, but it does show that
68//! uninitialized memory handling can be done entirely in safe code, being moderately ergonomic.
69//!
70//! (If this would be incorporated into `std::io::Read`, there would probably be a simpler unsafe
71//! function, that defaults to the safer wrapper.)
72
73#![cfg_attr(
74    feature = "nightly",
75    feature(maybe_uninit_array_assume_init, new_uninit)
76)]
77use core::mem::MaybeUninit;
78
79pub mod buffer;
80pub mod buffers;
81pub mod initializer;
82pub mod traits;
83pub mod wrappers;
84
85#[cfg(feature = "ioslice")]
86extern crate ioslice_ as ioslice;
87
88#[cfg(feature = "ioslice")]
89mod ioslice_impls;
90
91#[cfg(feature = "alloc")]
92extern crate alloc;
93
94#[inline]
95pub(crate) unsafe fn cast_slice_same_layout<A, B>(a: &[A]) -> &[B] {
96    core::slice::from_raw_parts(a.as_ptr() as *const B, a.len())
97}
98#[inline]
99pub(crate) unsafe fn cast_slice_same_layout_mut<A, B>(a: &mut [A]) -> &mut [B] {
100    core::slice::from_raw_parts_mut(a.as_mut_ptr() as *mut B, a.len())
101}
102
103/// Cast a slice of bytes into a slice of uninitialized bytes, pretending that it is uninitialized.
104/// This is completely safe, since `MaybeUninit` must have the exact same (direct) layout, like
105/// `u8` has. The downside with this is that the information about initializedness is lost; unless
106/// relying on unsafe code, the resulting slice can only be used to prove validity of the memory
107/// range.
108#[inline]
109pub fn cast_init_to_uninit_slice<U>(init: &[U]) -> &[MaybeUninit<U>] {
110    unsafe { cast_slice_same_layout(init) }
111}
112/// Cast a possibly uninitialized slice of bytes, into an initializied slice, assuming that it is
113/// initialized.
114///
115/// # Safety
116///
117/// The initialization variant must be upheld; that is, the caller must ensure that the buffer
118/// cannot contain any uninitialized data.
119#[inline]
120pub unsafe fn cast_uninit_to_init_slice<U>(uninit: &[MaybeUninit<U>]) -> &[U] {
121    cast_slice_same_layout(uninit)
122}
123
124/// Cast a mutable slice of bytes into a slice of uninitialized bytes, pretending that it is
125/// uninitialized. This is completely safe since they always have the same memory layout; however,
126/// the layout of the slices themselves must not be relied upon. The initializedness information is
127/// lost as part of this cast, but can be recovered when initializing again or by using unsafe
128/// code.
129///
130/// # Safety
131///
132/// This is unsafe, since it allows a slice which is borrowed for a lifetime possibly shorter than
133/// `'static`, to be reused after the `MaybeUninit` slice has had `MaybeUninit::uninit()` values
134/// written to it. For this to be safe, the caller must only write initialized bytes to the
135/// returned slice.
136///
137/// This function is only meant to be used in generic contexts, unlike
138/// [`cast_init_to_uninit_slice`], which is used more often when copying initialized bytes to
139/// uninitialized bytes.
140#[inline]
141pub unsafe fn cast_init_to_uninit_slice_mut<U>(init: &mut [U]) -> &mut [MaybeUninit<U>] {
142    cast_slice_same_layout_mut(init)
143}
144/// Cast a mutable slice of possibly initialized bytes into a slice of initialized bytes, assuming
145/// it is initialized.
146///
147/// # Safety
148///
149/// For this to be safe, the initialization invariant must be upheld, exactly like when reading.
150///
151/// __NOTE: This must not be used for initializing the buffer__. If unsafe code is still somehow,
152/// always initialize this by copying from _another_ MaybeUninit slice, or using [`std::ptr::copy`]
153/// or [`std::ptr::copy_nonoverlapping`].
154#[inline]
155pub unsafe fn cast_uninit_to_init_slice_mut<U>(uninit: &mut [MaybeUninit<U>]) -> &mut [U] {
156    cast_slice_same_layout_mut(uninit)
157}
158
159/// Fill a possibly uninitialized mutable slice of bytes, with the same `byte`, returning the
160/// initialized slice.
161#[inline]
162pub fn fill_uninit_slice<U: Copy>(slice: &mut [MaybeUninit<U>], item: U) -> &mut [U] {
163    unsafe {
164        slice.fill(MaybeUninit::new(item));
165        cast_uninit_to_init_slice_mut(slice)
166    }
167}