From 62a5a14775302b2a471de3b65b0f25a2c0bba965 Mon Sep 17 00:00:00 2001 From: danielhenrymantilla Date: Fri, 29 Nov 2019 00:54:54 +0100 Subject: [PATCH 1/5] Fixed unsoundness of using `&mut MaybeUninit` as the out type for `&mut T` --- Cargo.toml | 5 +- README.md | 52 +- src/extension_traits/as_out.rs | 155 ++++++ src/extension_traits/manually_drop_mut.rs | 67 +++ .../maybe_uninit.rs | 76 ++- src/extension_traits/mod.rs | 20 + src/{traits => extension_traits}/vec.rs | 44 +- src/lib.rs | 385 ++------------ src/out_references.rs | 495 ++++++++++++++++++ src/{ => read}/impls.rs | 21 +- src/read/mod.rs | 246 +++++++++ src/traits/mod.rs | 10 - src/traits/slice.rs | 93 ---- src/utils/macros.rs | 2 +- src/utils/mod.rs | 3 + src/utils/prelude.rs | 8 +- 16 files changed, 1149 insertions(+), 533 deletions(-) create mode 100644 src/extension_traits/as_out.rs create mode 100644 src/extension_traits/manually_drop_mut.rs rename src/{traits => extension_traits}/maybe_uninit.rs (61%) create mode 100644 src/extension_traits/mod.rs rename src/{traits => extension_traits}/vec.rs (78%) create mode 100644 src/out_references.rs rename src/{ => read}/impls.rs (85%) create mode 100644 src/read/mod.rs delete mode 100644 src/traits/mod.rs delete mode 100644 src/traits/slice.rs diff --git a/Cargo.toml b/Cargo.toml index 0050436..4ac5848 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,10 +18,9 @@ license = "MIT" require_unsafe_in_body = "0.2.0" [features] -chain = [] -downcast_as_ReadIntoUninit = [] nightly = [] -polonius-check = ["nightly"] +specialization = ["nightly"] +chain = [] default = [] diff --git a/README.md b/README.md index 73f36b3..891e6cd 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,16 @@ Note that there are other ways to trigger this UB without explicitely using ``` - this is exactly equivalent to calling `mem::uninitialized::()`, + which breaks the _validity_ invariant of `T` and thus causes + "instant UB". + + There currently only two exceptions / _valid_ use cases: + + - either [`type T = [MaybeUninit; N]`][`uninit_array`], + + - or `T` is an inhabited ZST (this may, however, break safety + invariants associated with the properties of the type, causing UB + once such broken invariant is witnessed). - yes, using [`MaybeUninit`] is more subtle than just changing a function call. @@ -58,8 +68,11 @@ Note that there are other ways to trigger this UB without explicitely using - ```rust let mut vec: Vec = Vec::with_capacity(100); // Fine unsafe { - vec.set_len(100); // UB: we have an uninitialized [u8; 100] in the heap + vec.set_len(100); // we have an uninitialized [u8; 100] in the heap + // This has broken the _safety_ invariant of `Vec`, but is not yet UB + // since no code has witnessed the broken state } + let heap_bytes: &[u8] = &*vec; // Witness the broken safety invariant: UB! ``` ## Instead, (you can) use [`MaybeUninit`] @@ -98,6 +111,7 @@ It is all about the _**delayed** initialization pattern_: let mut x = MaybeUninit::::uninit(); x = MaybeUninit::new(42); + assert_eq!(42, unsafe { x.assume_init() }); ``` - or through a raw `*mut T` pointer (contrary to Rust references, @@ -110,9 +124,24 @@ It is all about the _**delayed** initialization pattern_: unsafe { x.as_mut_ptr().write(42); + assert_eq!(42, x.assume_init()); } ``` + - or, if you use the tools of this crate, by upgrading the + `&mut MaybeUninit` into a "`&out T`" type called + [`Out`][`crate::prelude::Out`]: + + ```rust + #![forbid(unsafe_code)] // no unsafe! + use ::core::mem::MaybeUninit; + use ::uninit::prelude::*; + + let mut x = MaybeUninit::uninit(); + let at_init_x: &i32 = x.as_out::().write(42); + assert_eq!(42, *at_init_x); + ``` + 3. **Type-level upgrade** Once we know, for sure, that the memory has been initialized, we can @@ -153,7 +182,7 @@ pub trait Read { that is, there is no way to `.read()` into an unitialized buffer (it would require an api taking either a `(*mut u8, usize)` pair, or, equivalently and -by the way more ergonomically, a `&mut [MaybeUninit]`). +by the way more ergonomically, a [`&out [u8]`][`crate::prelude::OutSlice`]). # Enter `::uninit` @@ -163,7 +192,7 @@ So, the objective of this crate is double: For instance: - - [`uninit_byte_array!`] + - [`uninit_array!`] - [`Vec::reserve_uninit`] @@ -176,17 +205,12 @@ So, the objective of this crate is double: - [`.init_with_copy_from_slice()`] -## Status - -This is currently at an realy stage, so it "only" includes -utilities to work with **uninitialized bytes** or integers. - [`Read`]: https://doc.rust-lang.org/1.36.0/std/io/trait.Read.html [`mem::uninitialized`]: https://doc.rust-lang.org/core/mem/fn.uninitialized.html [`MaybeUninit`]: https://doc.rust-lang.org/core/mem/union.MaybeUninit.html -[`.assume_init_by_ref()`]: https://docs.rs/uninit/0.1.0/uninit/trait.MaybeUninitExt.html#method.assume_init_by_ref -[`.assume_init_by_mut()`]: https://docs.rs/uninit/0.1.0/uninit/trait.MaybeUninitExt.html#method.assume_init_by_mut -[`uninit_byte_array!`]: https://docs.rs/uninit/0.1.0/uninit/macro.uninit_byte_array.html -[`Vec::reserve_uninit`]: https://docs.rs/uninit/0.1.0/uninit/trait.VecReserveUninit.html#tymethod.reserve_uninit -[`.init_with_copy_from_slice()`]: https://docs.rs/uninit/0.1.0/uninit/trait.InitWithCopyFromSlice.html#tymethod.init_with_copy_from_slice -[`ReadIntoUninit`]: https://docs.rs/uninit/0.1.0/uninit/trait.ReadIntoUninit.html +[`.assume_init_by_ref()`]: `crate::extension_traits::MaybeUninitExt::assume_init_by_ref` +[`.assume_init_by_mut()`]: `crate::extension_traits::MaybeUninitExt::assume_init_by_mut` +[`uninit_array!`]: `uninit_array` +[`Vec::reserve_uninit`]: `crate::extension_traits::VecReserveUninit::reserve_uninit` +[`.init_with_copy_from_slice()`]: `crate::out_references::OutSlice::copy_from_slice` +[`ReadIntoUninit`]: `crate::read::ReadIntoUninit` diff --git a/src/extension_traits/as_out.rs b/src/extension_traits/as_out.rs new file mode 100644 index 0000000..f270682 --- /dev/null +++ b/src/extension_traits/as_out.rs @@ -0,0 +1,155 @@ +use_prelude!(); + +use ::core::mem::ManuallyDrop; + +#[cfg(doc)] +use crate::extension_traits::ManuallyDropMut; + +use private::Is; +mod private { + pub trait Is { type Eq : ?Sized; } + impl Is for T { type Eq = T; } +} + +/// Helper / extension trait to convert a `&mut _` into a `&out T` by calling +/// `.as_out::()` on it. +/// +/// By autoref, this means that you can even just extract a `&out T` reference +/// out of a `mut` element simply by calling `.as_out::()` on it. +/// +/// There is, however, one restriction: to be able to call `.as_out::<_>()` on +/// something, it needs to be either `Copy`, or a value wrapped in a +/// [`MaybeUninit`] / a [`ManuallyDrop`]. +/// +/// - (or a slice of such, in which case it yields a +/// [fat `&out` reference][`OutSlice`]) +/// +/// This is by design. Indeed, [`Out`] references do not call the destructor +/// of the overwritten element (since it may not be initialized). +/// This could cause memory leaks when there is an initialized element with +/// [drop glue][`core::mem::needs_drop`]. +/// +/// To solve this limitation, one must explicitly call +/// [`.manually_drop_mut()`][`ManuallyDropMut::manually_drop_mut`] +/// to automagically transmute the `&mut _` reference into a +/// `&mut ManuallyDrop<_>`. +/// +/// # Examples +/// +/// ```rust +/// use ::uninit::prelude::*; +/// +/// let mut x = 0; +/// x.as_out::().write(42); +/// +/// let mut y = ::core::mem::MaybeUninit::uninit(); +/// y.as_out::().write(42); +/// let y = unsafe { y.assume_init() }; +/// +/// assert_eq!(x, y); +/// ``` +pub +trait AsOut { + type Out; + + fn as_out> (self: Self) + -> Self::Out + ; +} + +impl<'out, T : 'out> AsOut for &'out mut MaybeUninit { + type Out = Out<'out, T>; + + #[inline] + fn as_out> (self: &'out mut MaybeUninit) + -> Out<'out, T> + { + self.into() + } +} + +impl<'out, T : 'out> AsOut for &'out mut T +where + T : Copy, +{ + type Out = Out<'out, T>; + + #[inline] + fn as_out> (self: &'out mut T) + -> Out<'out, T> + { + self.into() + } +} + +impl<'out, T : 'out> AsOut<[T]> for &'out mut [MaybeUninit] { + type Out = OutSlice<'out, T>; + + #[inline] + fn as_out> (self: &'out mut [MaybeUninit]) + -> OutSlice<'out, T> + { + self.into() + } +} + +impl<'out, T : 'out> AsOut<[T]> for &'out mut [T] +where + T : Copy, +{ + type Out = OutSlice<'out, T>; + + #[inline] + fn as_out> (self: &'out mut [T]) + -> OutSlice<'out, T> + { + self.into() + } +} + +impl<'out, T : 'out> AsOut for &'out mut ManuallyDrop { + type Out = Out<'out, T>; + + #[inline] + fn as_out> (self: &'out mut ManuallyDrop) + -> Out<'out, T> + { + self.into() + } +} + +impl<'out, T : 'out> AsOut<[T]> for &'out mut [ManuallyDrop] { + type Out = OutSlice<'out, T>; + + #[inline] + fn as_out> (self: &'out mut [ManuallyDrop]) + -> OutSlice<'out, T> + { + self.into() + } +} + +macro_rules! impl_arrays {( $($N:tt)* ) => ($( + impl<'out, T : 'out> AsOut<[T]> for &'out mut [MaybeUninit; $N] { + type Out = OutSlice<'out, T>; + + #[inline] + fn as_out> (self: Self) + -> OutSlice<'out, T> + { + From::from(&mut self[..]) + } + } +)*)} + +impl_arrays! { + 0 1 2 3 4 5 6 7 8 + 9 10 11 12 13 14 15 16 + 17 18 19 20 21 22 23 24 + 25 26 27 28 29 30 31 32 + 33 34 35 36 37 38 39 40 + 41 42 43 44 45 46 47 48 + 49 50 51 52 53 54 55 56 + 57 58 59 60 61 62 63 64 + 128 256 512 1024 +} diff --git a/src/extension_traits/manually_drop_mut.rs b/src/extension_traits/manually_drop_mut.rs new file mode 100644 index 0000000..76bf215 --- /dev/null +++ b/src/extension_traits/manually_drop_mut.rs @@ -0,0 +1,67 @@ +use ::core::mem::ManuallyDrop; + +#[cfg(doc)] use crate::Out; + +/// An extension trait providing a cast to the [`ManuallyDrop`] type. +/// +/// This is useful if you want to use an [`Out`] reference to something that +/// is not `Copy` (potentially because it has drop glue, in which case you +/// either don't mind leaking / skipping that drop glue, or you know you will +/// be manually handling it). +/// +/// # Example +/// +/// ```rust,compile_fail +/// use ::core::cell::Cell; +/// use ::uninit::prelude::{AsOut, ManuallyDropMut}; +/// +/// let mut cell = Cell::new(0); +/// cell.as_out::>().write(Cell::new(42)); // Error, not `Copy` +/// assert_eq!(cell.get(), 42); +/// ``` +/// +/// ```rust +/// use ::core::cell::Cell; +/// use ::uninit::prelude::{AsOut, ManuallyDropMut}; +/// +/// let mut cell = Cell::new(0); +/// cell.manually_drop_mut().as_out::>().write(Cell::new(42)); // OK +/// assert_eq!(cell.get(), 42); +/// ``` +pub +trait ManuallyDropMut { + type Ret : ?Sized; + + fn manually_drop_mut (self: &'_ mut Self) + -> &'_ mut Self::Ret + ; +} + +impl ManuallyDropMut for [T] { + type Ret = [ManuallyDrop]; + + fn manually_drop_mut<'__> (self: &'__ mut [T]) + -> &'__ mut [ManuallyDrop] + { + let len = self.len(); + unsafe { + // Safety: `ManuallyDrop` is `#[repr(transparent)]` + ::core::slice::from_raw_parts_mut( + self.as_mut_ptr().cast(), len, + ) + } + } +} + +impl ManuallyDropMut for T { + type Ret = ManuallyDrop; + + fn manually_drop_mut<'__> (self: &'__ mut T) + -> &'__ mut ManuallyDrop + { + unsafe { + // Safety: `ManuallyDrop` is `#[repr(transparent)]` + ::core::mem::transmute(self) + } + } +} diff --git a/src/traits/maybe_uninit.rs b/src/extension_traits/maybe_uninit.rs similarity index 61% rename from src/traits/maybe_uninit.rs rename to src/extension_traits/maybe_uninit.rs index 7021c6c..104074d 100644 --- a/src/traits/maybe_uninit.rs +++ b/src/extension_traits/maybe_uninit.rs @@ -5,6 +5,38 @@ use_prelude!(); /// This is currently only implemented for [`Copy`] types, since the /// semantics when [`drop` glue][`mem::needs_drop`] is involved are less /// easy to handle correctly (danger of leaking memory). +/// +/// # `from_mut`? +/// +/// The conversion `&mut T` to `&mut MaybeUninit` is actually unsound, +/// since there is nothing preventing the obtained `mut` reference to be used +/// to overwrite the pointee with `MaybeUninit::uninit()`, _i.e._, an +/// uninitialised (and thus garbage) value: +/// +/// ```rust,no_run +/// use ::core::mem::{MaybeUninit, transmute}; +/// +/// unsafe fn from_mut (it: &'_ mut T) -> &'_ mut MaybeUninit +/// { +/// transmute(it) +/// } +/// +/// let mut x = Box::new(42); +/// let at_x_uninit: &mut MaybeUninit> = unsafe { +/// // If this were safe... +/// from_mut(&mut x) +/// }; +/// // Then safe code would be able to do this: +/// *at_x_uninit = MaybeUninit::uninit(); // <--------+ +/// // drop(x); // drops an uninitialized value! UB --+ +/// ``` +/// +/// The author of the crate did overlook that and offered such transformation +/// within a non-`unsafe` function, leading to an unsound function. And now, to +/// that version being yanked. +/// +/// The correct way to do this now is through +/// [the `&out` reference abstraction][`crate::out_references`]. pub trait MaybeUninitExt { type T : ?Sized; @@ -28,10 +60,6 @@ trait MaybeUninitExt { fn from_ref (init_ref: &'_ Self::T) -> &'_ Self ; - - fn from_mut (init_mut: &'_ mut Self::T) - -> &'_ mut Self - ; } #[require_unsafe_in_bodies] @@ -72,17 +100,6 @@ impl MaybeUninitExt for MaybeUninit { mem::transmute(some_ref) } } - - fn from_mut (some_mut_ref: &'_ mut Self::T) - -> &'_ mut Self - { - unsafe { - // # Safety - // - // - Same memory layout, bounded lifetimes, same mut-ness - mem::transmute(some_mut_ref) - } - } } #[require_unsafe_in_bodies] @@ -97,7 +114,11 @@ impl MaybeUninitExt for [MaybeUninit] { // # Safety // // - Same memory layout, bounded lifetimes, same mut-ness - mem::transmute(self) + let len = self.len(); + slice::from_raw_parts( + self.as_ptr().cast(), + len, + ) } } @@ -109,7 +130,11 @@ impl MaybeUninitExt for [MaybeUninit] { // # Safety // // - Same memory layout, bounded lifetimes, same mut-ness - mem::transmute(self) + let len = self.len(); + slice::from_raw_parts_mut( + self.as_mut_ptr().cast(), + len, + ) } } @@ -120,18 +145,11 @@ impl MaybeUninitExt for [MaybeUninit] { // # Safety // // - Same memory layout, bounded lifetimes, same mut-ness - mem::transmute(slice) - } - } - - fn from_mut (slice: &'_ mut Self::T) - -> &'_ mut Self - { - unsafe { - // # Safety - // - // - Same memory layout, bounded lifetimes, same mut-ness - mem::transmute(slice) + let len = slice.len(); + slice::from_raw_parts( + slice.as_ptr().cast(), + len, + ) } } } diff --git a/src/extension_traits/mod.rs b/src/extension_traits/mod.rs new file mode 100644 index 0000000..aee5ae1 --- /dev/null +++ b/src/extension_traits/mod.rs @@ -0,0 +1,20 @@ +pub use self::as_out::{ + AsOut, +}; +mod as_out; + +pub use self::manually_drop_mut::{ + ManuallyDropMut, +}; +mod manually_drop_mut; + +pub use self::maybe_uninit::{ + MaybeUninitExt, +}; +mod maybe_uninit; + +pub use self::vec::{ + VecExtendFromReader, + VecReserveUninit, +}; +mod vec; diff --git a/src/traits/vec.rs b/src/extension_traits/vec.rs similarity index 78% rename from src/traits/vec.rs rename to src/extension_traits/vec.rs index 67a5913..ef4592d 100644 --- a/src/traits/vec.rs +++ b/src/extension_traits/vec.rs @@ -2,27 +2,30 @@ use crate::*; use ::core::slice; /// Extension trait for [`Vec`], that reserves extra uninitialized memory for -/// it, and **returns a mutable handle on those extra (uninitialized) bytes**. +/// it, and **returns a mutable handle on those extra (uninitialized) elements**. /// /// # Example /// /// ```rust /// # use ::core::mem::MaybeUninit; -/// use ::uninit::{InitWithCopyFromSlice, VecReserveUninit}; +/// use ::uninit::prelude::*; /// /// let mut vec = b"Hello, ".to_vec(); /// const WORLD: &[u8] = b"World!"; -/// let uninit_start: *mut u8 = vec.as_mut_ptr().wrapping_add(vec.len()); /// /// let extra: &mut [MaybeUninit] = vec.reserve_uninit(WORLD.len()); -/// assert_eq!(extra.as_mut_ptr() as *mut u8, uninit_start); +/// extra.as_out::<[u8]>().copy_from_slice(WORLD); +/// +/// // `.reserve_uninit()` guarantees the following properties: /// assert_eq!(extra.len(), WORLD.len()); +/// let extra_start: *mut u8 = extra.as_mut_ptr().cast(); +/// let uninit_start: *mut u8 = vec.as_mut_ptr().wrapping_add(vec.len()); +/// assert_eq!(extra_start, uninit_start); /// -/// extra.init_with_copy_from_slice(WORLD); /// unsafe { /// // # Safety /// // -/// // - `.init_with_copy_from_slice()` contract guarantees initialization +/// // - `.copy_from_slice()` contract guarantees initialization /// // of `extra`, which, in turn, from `reserve_uninit`'s contract, /// // leads to the `vec` extra capacity having been initialized. /// vec.set_len(vec.len() + WORLD.len()); @@ -34,28 +37,29 @@ use ::core::slice; /// ``` pub trait VecReserveUninit { + type Item; + fn reserve_uninit (self: &'_ mut Self, additional: usize) - -> &'_ mut [MaybeUninit] + -> &'_ mut [MaybeUninit] ; } -impl VecReserveUninit for Vec { +impl VecReserveUninit for Vec { + type Item = T; + fn reserve_uninit (self: &'_ mut Self, additional: usize) - -> &'_ mut [MaybeUninit] + -> &'_ mut [MaybeUninit] { self.reserve(additional); unsafe { // # Safety // - // 1. Vec contract guarantees that after a call to `.reserve(n)` + // - Vec contract guarantees that after a call to `.reserve(n)` // at least `n` uninitialized elements after the end of the // Vec's current length can be soundly written to. - // - // 2. `Vec` also guarantees that `self.len()` does not overflow - // `isize`, so neither does `self.len() * 1` - slice::from_raw_parts_mut( // 1. - utils::ptr_cast_mut::>( + slice::from_raw_parts_mut( + utils::ptr_cast_mut::>( self.as_mut_ptr() - .add(self.len()) // 2. + .wrapping_add(self.len()) ), additional, ) @@ -63,7 +67,6 @@ impl VecReserveUninit for Vec { } } - /// Extension trait for [`Vec`], that grows the vec by a _bounded_ amount of /// bytes, obtained when reading from `R`. /// @@ -73,7 +76,7 @@ impl VecReserveUninit for Vec { /// # Example /// /// ```rust -/// use ::uninit::VecExtendFromReader; +/// use ::uninit::read::VecExtendFromReader; /// /// let mut reader = &b"World!"[..]; /// let mut vec = b"Greetings, ".to_vec(); @@ -101,12 +104,11 @@ impl VecExtendFromReader for Vec { mut reader: R, ) -> io::Result<()> { - let buf: &mut [MaybeUninit] = self.reserve_uninit(count); + let buf: OutSlice = self.reserve_uninit(count).into(); let buf: &mut [u8] = reader.read_into_uninit_exact(buf)?; let count: usize = buf.len(); debug_assert_eq!( - buf .as_mut_ptr() - , + buf.as_mut_ptr(), self.as_mut_ptr() .wrapping_add(self.len()) , diff --git a/src/lib.rs b/src/lib.rs index 967b188..87ad3ab 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,384 +1,73 @@ #![cfg_attr(feature = "nightly", - feature(external_doc), + feature(doc_cfg, external_doc), doc(include = "../README.md"), )] +#![cfg_attr(feature = "specialization", + feature(specialization), +)] #[macro_use] extern crate require_unsafe_in_body; #[macro_use] mod utils; -#[path = "utils/prelude.rs"] -mod prelude; +pub mod prelude { + #[doc(no_inline)] + pub use crate::{ + extension_traits::{ + AsOut, + ManuallyDropMut, + VecReserveUninit, + }, + out_references::{ + Out, + OutSlice, + }, + uninit_array, + }; +} use_prelude!(); -mod impls; +pub +mod extension_traits; + +pub +mod read; -pub use self::traits::{ - MaybeUninitExt, - slice::{ - InitWithCopyFromSlice, - }, - vec::{ - VecExtendFromReader, - VecReserveUninit, - }, -}; -mod traits; +pub +mod out_references; #[doc(hidden)] pub use ::core; /// Sets up an inline / stack-allocated array of -/// [uninitialized bytes][`MaybeUninit`]. +/// [uninitialized][`MaybeUninit`] elements. /// /// # Example /// /// ```rust -/// use ::uninit::{ReadIntoUninit, uninit_byte_array}; +/// use ::uninit::{prelude::*, read::ReadIntoUninit}; /// /// let mut reader = &b"Hello, World!"[..]; -/// let mut array = uninit_byte_array![_; 4]; // : [MaybeUninit; 4] +/// let mut backing_array = uninit_array![u8; 4]; // : [MaybeUninit; 4] +/// let buf = OutSlice::from(&mut backing_array[..]); /// assert_eq!( -/// reader.read_into_uninit_exact(&mut array).unwrap(), +/// reader.read_into_uninit_exact(buf).unwrap(), /// b"Hell", /// ); /// ``` #[macro_export] -macro_rules! uninit_byte_array {( - _; $count:expr +macro_rules! uninit_array {( + $T:ty ; $count:expr ) => ({ use $crate::core::mem::MaybeUninit; - type Array = [MaybeUninit; $count]; + const __UNINIT_ARRAY_COUNT__: usize = $count; unsafe { // # Safety // - // - mem::uninitialized::<[MaybeUninit<_>; _]>() is sound. - MaybeUninit::::uninit().assume_init() + // - `mem::uninitialized::<[MaybeUninit<_>; _]>()` is sound. + MaybeUninit::<[MaybeUninit<$T>; __UNINIT_ARRAY_COUNT__]>::uninit() + .assume_init() } })} - -/// Basically [`AsMut`]`<[`[`MaybeUninit`]`]>` but with a more explicit -/// name. -pub -trait AsUninitSliceMut { - fn as_uninit_slice_mut (self: &'_ mut Self) - -> &'_ mut [MaybeUninit] - ; -} - -/// Init bytes can be seen as uninit. -impl AsUninitSliceMut for [u8] -{ - #[inline] - fn as_uninit_slice_mut (self: &'_ mut Self) - -> &'_ mut [MaybeUninit] - { - <[MaybeUninit]>::from_mut(self) - } -} - -/// Init bytes can be seen as uninit. -impl AsUninitSliceMut for Vec -{ - #[inline] - fn as_uninit_slice_mut (self: &'_ mut Self) - -> &'_ mut [MaybeUninit] - { - <[MaybeUninit]>::from_mut(self) - } -} - -/// Identity -impl AsUninitSliceMut for [MaybeUninit] { - /// This can be trusted to return the same slice as given in input, - /// so that initializing the returned slice is guaranteed to have initialized - /// the input slice. - #[inline] - fn as_uninit_slice_mut<'__> (self: &'__ mut [MaybeUninit]) - -> &'__ mut [MaybeUninit] - { - self - } -} - -#[cfg(not(feature = "downcast_as_ReadIntoUninit"))] -use io::Read; - -#[cfg(feature = "downcast_as_ReadIntoUninit")] -// With specialization this can be overriden -impl Read for R { - #[inline] - // default - fn downcast_read_uninit (self: &'_ mut Self) - -> Option<&'_ mut dyn ReadIntoUninit> - { - None - } -} - -#[cfg(feature = "downcast_as_ReadIntoUninit")] -pub -trait Read: io::Read { - #[allow(bad_style)] - #[inline] - fn downcast_as_ReadIntoUninit (self: &'_ mut Self) - -> Option<&'_ mut dyn ReadIntoUninit> - { - None - } - - fn mb_read_into_uninit<'buf> ( - self: &'_ mut Self, - buf: &'buf mut [MaybeUninit], - ) -> io::Result<&'buf mut [u8]> - { - if let Some(uninit_reader) = self.downcast_as_ReadIntoUninit() { - uninit_reader - .read_into_uninit(buf) - } else { - buf.iter_mut().for_each(|at_uninit_byte| { - *at_uninit_byte = MaybeUninit::new(0); - }); - let buf: &mut [u8] = unsafe { - // # Safety - // - // - buf has been initialized - <[MaybeUninit]>::assume_init_by_mut(buf) - }; - let n = self.read(buf)?; - Ok(&mut buf[.. n]) - } - } -} - -/// Trait for a [`Read`](https://doc.rust-lang.org/std/io/trait.Read.html)able -/// type that can output the bytes read into -/// [uninitialised memory][`MaybeUninit`]. -/// -/// # Safety -/// -/// The trait is marked `unsafe` because it **needs to guarantee** that: -/// -/// - `if let Ok(init_buf) = self.read_into_uninit(buf)`, then -/// `init_buf` is a prefix slice of `buf`. -/// -/// - this property is equivalent to: -/// -/// `init_buf.as_mut_ptr() == buf.as_mut_ptr() as *mut u8` and -/// `init_buf.len() <= buf.len()` -/// -/// - as well as: -/// -/// **`buf[.. init_buf.len()]` is sound to `assume_init`** -/// -/// `unsafe` code can assume this property to skip checks or manual -/// initialization, and that's why incorrectly `impl`-ementing this marker -/// trait can compromise memory safety. -pub -unsafe -trait ReadIntoUninit : Read -{ - /// "Read into uninitialized memory" logic. - #[inline] - fn read_into_uninit<'buf> ( - self: &'_ mut Self, - buf: &'buf mut [MaybeUninit], - ) -> io::Result<&'buf mut [u8]> - ; - - /// "Read into uninitialized memory" logic. - // # Safety - // - delegates to `self.read_into_uninit()` calls. - #[inline] - fn read_into_uninit_exact<'buf> ( - self: &'_ mut Self, - buf: &'buf mut [MaybeUninit], - ) -> io::Result<&'buf mut [u8]> - { - { - let mut buf = &mut *buf; - while buf.is_empty().not() { - match self.read_into_uninit(buf).map(|x| x.len()) { - | Ok(0) => break, - | Ok(n) => buf = &mut buf[n ..], - | Err(ref e) - if e.kind() == io::ErrorKind::Interrupted - => {}, - | Err(e) => return Err(e), - } - } - if buf.is_empty().not() { - return Err(io::Error::new( - io::ErrorKind::UnexpectedEof, - "failed to fill whole buffer", - )); - } - } - Ok(unsafe { - // # Safety - // - // - this is the "concatenation" of all the "buf[.. n]" - // initialisation witnesses. - <[MaybeUninit]>::assume_init_by_mut(buf) - }) - } - - #[cfg(feature = "chain")] - fn chain ( - self: Self, - next: R, - ) -> Chain - where - Self : Sized, - { - Chain { first: self, second: next, first_done: false } - } - -} - -#[allow(unused_macros)] -macro_rules! auto_impl {( - #[derived_from(ReadIntoUninit)] - impl [$($generics:tt)*] io::Read for $T:ty - where - $($where_clause:tt)* -) => ( - impl<$($generics)*> io::Read for $T - where - $($where_clause)* - { - #[inline] - fn read ( - self: &'_ mut Self, - buf: &'_ mut [u8], - ) -> io::Result - { - ::read_into_uninit( - self, - buf.as_uninit_slice_mut(), - ).map(|x| x.len()) - } - } -)} - -#[cfg(feature = "chain")] -mod chain { - use super::*; - - #[derive(Debug)] - pub - struct Chain - where - R1 : ReadIntoUninit, - R2 : ReadIntoUninit, - { - first: R1, - second: R2, - first_done: bool, - } - - impl Chain - where - R1 : ReadIntoUninit, - R2 : ReadIntoUninit, - { - pub - fn into_inner (self: Self) - -> (R1, R2) - { - let Self { first, second, ..} = self; - (first, second) - } - - pub - fn get_ref (self: &'_ Self) - -> (&'_ R1, &'_ R2) - { - let Self { first, second, ..} = self; - (first, second) - } - } - - /// This test verifies the soundness of the lifetime erasure in - /// ` as ReadIntoUninit>::read_into_uninit()` - #[cfg(all( - test, - not(feature = "polonius-check"), - ))] - #[test] - fn check () - { - assert!( - ::std::process::Command::new("cargo") - .arg("+nightly") - .arg("rustc") - .arg("--features") - .arg( - [ - "chain", - "polonius-check", - ].join(" ") - ) - .arg("--") - .arg("-Zpolonius") - .status() - .unwrap() - .success() - ); - } - - unsafe - impl ReadIntoUninit for Chain - where - R1 : ReadIntoUninit, - R2 : ReadIntoUninit, - { - fn read_into_uninit<'buf> ( - self: &'_ mut Self, - buf: &'buf mut [MaybeUninit], - ) -> io::Result<&'buf mut [u8]> - { - if buf.len() == 0 { - return Ok(unsafe { - // # Safety - // - // - since it has `0` elements, - // it does have `0` initialized elements. - <[MaybeUninit]>::assume_init_by_mut(buf) - }) - } - if self.first_done.not() { - #[cfg(not(feature = "polonius-check"))] - let buf: &mut [MaybeUninit] = unsafe { - // Lifetime hack while waiting for polonius - // - // # Safety - // - // - `cargo rustc --features polonius-check -- -Zpolonius` - // passes. - slice::from_raw_parts_mut( - buf.as_mut_ptr(), - buf.len(), - ) - }; - let buf = self.first.read_into_uninit(buf)?; - if buf.is_empty() { - self.first_done = true; - } else { - return Ok(buf); - } - } - self.second.read_into_uninit(buf) - } - } - - auto_impl!( - #[derived_from(ReadIntoUninit)] - impl[R1, R2] io::Read for Chain - where - R1 : ReadIntoUninit, - R2 : ReadIntoUninit, - ); -} diff --git a/src/out_references.rs b/src/out_references.rs new file mode 100644 index 0000000..8cf26b3 --- /dev/null +++ b/src/out_references.rs @@ -0,0 +1,495 @@ +use ::core::{ + mem::{self, + ManuallyDrop, + MaybeUninit, + }, + ptr, + slice, +}; + +/// Wrapper expressing the semantics of `&out T` references +/// (slim pointer (`T : Sized`)). +/// +/// In other words, this has the semantics of `&'out mut MaybeUninit`, but +/// for the ability to write garbage (`MaybeUninit::uninit()`) into it +/// (else coercing `&mut T` to `&out T = Out` would be unsound). +/// +/// This means that the reference may point to uninitialized memory (or not), +/// and thus that writes to the pointee will not call the `.drop()` destructor. +/// +/// This type can be [trivially constructed][`crate::AsOut`] from: +/// +/// - a `&'out mut MaybeUninit` (main point of the type), +/// +/// - a `&'out mut T` (to keep the ergonomics of being able to overwrite an +/// already initialized value). +/// +/// - To avoid "accidentally" leaking memory in this second case, +/// either `T` must be `Copy` (sufficient condition to prove there is +/// no drop glue), or you must first call +/// [`.manually_drop_mut()`][`crate::ManuallyDropMut`] +/// before the [`.as_out()`][`crate::AsOut`] "coercion". +#[repr(transparent)] +pub +struct Out<'out, T : 'out> ( + &'out mut MaybeUninit, +); + +impl<'out, T : 'out> From<&'out mut T> for Out<'out, T> +where + T : Copy, // prevent accidentally leaking memory +{ + #[inline] + fn from (p: &'out mut T) + -> Out<'out, T> + { + Out(unsafe { + // # Safety + // + // - The API does not allow to write `MaybeUninit::uninit()` into + // the pointee. + mem::transmute(p) + }) + } +} + +/// For non-`Copy` types, explicitely transmuting the `mut` reference into one +/// that points to a `ManuallyDrop` is required, so as to express how likely it +/// is that memory be leaked. +impl<'out, T : 'out> From<&'out mut ManuallyDrop> for Out<'out, T> { + #[inline] + fn from (p: &'out mut ManuallyDrop) + -> Out<'out, T> + { + Out(unsafe { + // # Safety + // + // - The API does not allow to write `MaybeUninit::uninit()` into + // the pointee. + mem::transmute(p) + }) + } +} + +impl<'out, T : 'out> From<&'out mut MaybeUninit> for Out<'out, T> { + #[inline] + fn from (p: &'out mut MaybeUninit) + -> Out<'out, T> + { + Out(p) + } +} + +impl<'out, T : 'out> Out<'out, T> { + #[inline] + pub + fn new (p: impl Into>) + -> Self + { + p.into() + } + + #[inline] + pub + fn write (self: Out<'out, T>, value: T) + -> &'out mut T + { + *self.0 = MaybeUninit::new(value); + unsafe { + // Safety: pointee has just been write-initialized. + self.assume_init() + } + } + + #[inline] + pub + fn replace (self: Out<'out, T>, value: T) + -> (MaybeUninit, &'out mut T) + { + ( + mem::replace(self.0, MaybeUninit::new(value)), + unsafe { + // Safety: pointee has just been write-initialized. + self.assume_init() + }, + ) + } + + #[inline] + pub + fn as_mut_ptr (self: &'_ mut Out<'out, T>) + -> *mut T + { + self.0.as_mut_ptr() + } + + #[inline] + pub + fn reborrow<'reborrow> (self: &'reborrow mut Out<'out, T>) + -> Out<'reborrow, T> + where + 'out : 'reborrow, + { + Out(&mut *self.0) + } + + /// Shorthand for [`.reborrow()`][`Out::reborrow`] + #[inline] + pub + fn r<'reborrow> (self: &'reborrow mut Out<'out, T>) + -> Out<'reborrow, T> + where + 'out : 'reborrow, + { + self.reborrow() + } + + #[inline] + pub + unsafe + fn assume_init (mut self: Out<'out, T>) -> &'out mut T + { + &mut *self.as_mut_ptr() + } +} + +/// Wrapper expressing the semantics of `&out [T]` references +/// (fat pointer to a slice). +/// +/// In other words, this has the semantics of `&'out mut [MaybeUninit]`, but +/// for the ability to write garbage (`MaybeUninit::uninit()`) into it +/// (else coercing `&mut [T]` to `&out [T] = OutSlice` would be unsound). +/// +/// This means that the reference may point to uninitialized memory (or not), +/// and thus that writes to the pointee will not call the `.drop()` destructor. +/// +/// This type can be [trivially constructed][`crate::AsOut`] from: +/// +/// - a `&'out mut [MaybeUninit]` (main point of the type), +/// +/// - a `&'out mut [T]` (to keep the ergonomics of being able to overwrite an +/// already initialized value). +/// +/// - To avoid "accidentally" leaking memory in this second case, +/// either `T` must be `Copy` (sufficient condition to prove there is +/// no drop glue), or you must first call +/// [`.manually_drop_mut()`][`crate::ManuallyDropMut`] +/// before the [`.as_out()`][`crate::AsOut`] "coercion". +#[repr(transparent)] +pub +struct OutSlice<'out, T : 'out> ( + &'out mut [MaybeUninit], +); + +impl<'out, T : 'out> From<&'out mut [T]> for OutSlice<'out, T> +where + T : Copy, +{ + #[inline] + fn from (slice: &'out mut [T]) + -> OutSlice<'out, T> + { + let len = slice.len(); + OutSlice(unsafe { + // # Safety + // + // - The API does not allow to write `MaybeUninit::uninit()` into + // the pointee. + slice::from_raw_parts_mut( + slice.as_mut_ptr().cast(), + len, + ) + }) + } +} + +impl<'out, T : 'out> From<&'out mut [ManuallyDrop]> for OutSlice<'out, T> { + #[inline] + fn from (slice: &'out mut [ManuallyDrop]) + -> OutSlice<'out, T> + { + let len = slice.len(); + OutSlice(unsafe { + // # Safety + // + // - The API does not allow to write `MaybeUninit::uninit()` into + // the pointee. + slice::from_raw_parts_mut( + slice.as_mut_ptr().cast(), + len, + ) + }) + } +} + +impl<'out, T : 'out> From<&'out mut [MaybeUninit]> for OutSlice<'out, T> { + #[inline] + fn from (slice: &'out mut [MaybeUninit]) + -> OutSlice<'out, T> + { + OutSlice(slice) + } +} + +impl<'out, T : 'out> OutSlice<'out, T> { + #[inline] + pub + fn new (slice: impl Into>) + -> Self + { + slice.into() + } + + #[inline] + pub + fn as_mut_ptr (self: &'_ mut Self) + -> *mut T + { + self.0.as_mut_ptr().cast() + } + + #[inline] + pub + fn reborrow<'reborrow> (self: &'reborrow mut OutSlice<'out, T>) + -> OutSlice<'reborrow, T> + where + 'out : 'reborrow, + { + OutSlice(&mut *self.0) + } + + /// Shorthand for [`.reborrow()`][`OutSlice::reborrow`] + #[inline] + pub + fn r<'reborrow> (self: &'reborrow mut OutSlice<'out, T>) + -> OutSlice<'reborrow, T> + where + 'out : 'reborrow, + { + self.reborrow() + } + + #[inline] + pub + fn idx (self: OutSlice<'out, T>, idx: Index) + -> Option + where + Index : SliceIndex<'out, T>, + { + impl<'out, T : 'out> SliceIndex<'out, T> for usize { + type Output = Out<'out, T>; + + #[inline] + fn idx (self: usize, slice: OutSlice<'out, T>) + -> Option> + { + slice.0.get_mut(self).map(Out::new) + } + } + + macro_rules! impl_SliceIndex {( + $($Range:ty),+ $(,)? + ) => ( + $( + impl<'out, T : 'out> SliceIndex<'out, T> for $Range { + type Output = OutSlice<'out, T>; + + #[inline] + fn idx (self: Self, slice: OutSlice<'out, T>) + -> Option> + { + slice.0.get_mut(self).map(OutSlice::new) + } + } + )* + )} + + impl_SliceIndex! { + // a .. b + ::core::ops::Range, + // a ..= b + ::core::ops::RangeInclusive, + // a .. + ::core::ops::RangeFrom, + // .. b + ::core::ops::RangeTo, + // ..= b + ::core::ops::RangeToInclusive, + // .. + ::core::ops::RangeFull, + } + + idx.idx(self) + } + + #[inline] + pub + unsafe + fn assume_init (mut self: OutSlice<'out, T>) -> &'out mut [T] + { + let len = self.len(); + slice::from_raw_parts_mut( + self.as_mut_ptr(), + len, + ) + } + + /// Initialize the buffer with a copy from another (already initialized) + /// buffer. + /// + /// It returns a read-writable slice to the initialized bytes for + /// convenience (automatically + /// [`assume_init`][`OutSlice::assume_init`]-ed). + /// + /// # Panic + /// + /// The function panics if the slices' lengths are not equal. + /// + /// # Guarantees + /// + /// A non-`panic!`king return from this function **guarantees that the input + /// slice has been (successfully) initialized**, and that it is thus then + /// sound to `.assume_init()`. + /// + /// It also guarantees that the returned slice does correspond to the input + /// slice (_e.g._, for [`crate::ReadIntoUninit`]'s safety guarantees). + /// + /// # Example + /// + /// ```rust + /// # use ::core::mem::{self, MaybeUninit}; + /// use ::uninit::prelude::*; + /// + /// let mut array = uninit_array![_; 13]; + /// assert_eq!( + /// array.as_out::<[u8]>().copy_from_slice(b"Hello, World!"), + /// b"Hello, World!", + /// ); + /// // we can thus soundly `assume_init` our array: + /// let array = unsafe { + /// mem::transmute::< + /// [MaybeUninit; 13], + /// [ u8 ; 13], + /// >(array) + /// }; + /// assert_eq!( + /// array, + /// *b"Hello, World!", + /// ); + /// ``` + pub + fn copy_from_slice ( + mut self: OutSlice<'out, T>, + source_slice: &'_ [T], + ) -> &'out mut [T] + where + T : Copy, + { + assert_eq!( + self.len(), + source_slice.len(), + "`copy_from_slice()`: length mismatch", + ); + unsafe { + // # Safety + // + // - `T : Copy` + // + // - `OutSlice` is unaliased and thus guarantees no overlap; + // + // - `self[.. len]` is valid to write to; + // + // - `source_slice[.. len]` is valid to read (and copy) from. + ptr::copy_nonoverlapping( + source_slice.as_ptr(), + self.as_mut_ptr(), + self.len(), + ); + } + unsafe { + // # Safety + // + // - the `copy_nonoverlapping()` call guarantees that the buffer + // has been initialized. + self.assume_init() + } + } + + /// Fill the buffer with values from a function mapping indices to values. + /// + /// # Guarantees + /// + /// `factory` is guaranteed to be called in the `0 .. self.len()` order. + #[inline] + pub + fn fill_with ( + mut self: OutSlice<'out, T>, + mut factory: impl FnMut(usize) -> T, + ) -> &'out mut [T] + { + let len = self.len(); + (0 .. len).for_each(|i| { + self.r().idx(i).unwrap().write(factory(i)); + }); + unsafe { + // Safety: The `len` values of the buffer have been initialized + self.assume_init() + } + } + + #[inline] + pub + fn iter_out (self: &'_ mut OutSlice<'out, T>) + -> impl Iterator> + '_ + { + ::core::iter::from_fn({ + let mut slice = &mut *self.0; + move || Some({ + if slice.is_empty() { return None; } + let (first, rest) = + mem::replace(&mut slice, &mut []) + .split_at_mut(1) + ; + let first: &mut MaybeUninit = &mut first[0]; + slice = rest; + first.into() + }) + }) + } + + #[inline] + pub + fn split_at_out (self: OutSlice<'out, T>, idx: usize) + -> (OutSlice<'out, T>, OutSlice<'out, T> ) + { + let (left, right) = self.0.split_at_mut(idx); + (left.into(), right.into()) + } +} + +/// `Deref` into `[MaybeUninit]` to get access to the slice length related +/// getters. +impl<'out, T : 'out> ::core::ops::Deref for OutSlice<'out, T> { + type Target = [MaybeUninit]; + + #[inline] + fn deref (self: &'_ Self) + -> &'_ Self::Target + { + &*self.0 + } +} + +use private::SliceIndex; +mod private { + use super::*; + + pub + trait SliceIndex<'out, T> { + type Output : 'out; + + fn idx (self: Self, slice: OutSlice<'out, T>) + -> Option + ; + } +} diff --git a/src/impls.rs b/src/read/impls.rs similarity index 85% rename from src/impls.rs rename to src/read/impls.rs index cd9f71c..9dae785 100644 --- a/src/impls.rs +++ b/src/read/impls.rs @@ -10,7 +10,7 @@ impl ReadIntoUninit for &'_ mut R { #[inline] fn read_into_uninit<'buf> ( self: &'_ mut Self, - buf: &'buf mut [MaybeUninit], + buf: OutSlice<'buf, u8>, ) -> io::Result<&'buf mut [u8]> { (**self).read_into_uninit(buf) @@ -26,7 +26,7 @@ unsafe impl ReadIntoUninit for &'_ [u8] { fn read_into_uninit<'buf> ( self: &'_ mut Self, - buf: &'buf mut [MaybeUninit], + mut buf: OutSlice<'buf, u8>, ) -> io::Result<&'buf mut [u8]> { let count = ::std::cmp::min(buf.len(), self.len()); @@ -37,7 +37,7 @@ impl ReadIntoUninit for &'_ [u8] { // `copy_from_slice` will generally expand to a call to `memcpy`, and // for a single byte the overhead is significant. if count == 1 { - buf[0] = MaybeUninit::new(to_copy[0]); + buf.reborrow().idx(0).unwrap().write(to_copy[0]); } else { unsafe { // # Safety @@ -50,13 +50,8 @@ impl ReadIntoUninit for &'_ [u8] { // // - they cannot overlap given the `&mut` access on `buf` ptr::copy_nonoverlapping::( - to_copy - .as_ptr() - , - utils::ptr_cast_mut::, u8>( - buf - .as_mut_ptr() - ) , + to_copy.as_ptr(), + buf.as_mut_ptr(), count, ); } @@ -65,7 +60,7 @@ impl ReadIntoUninit for &'_ [u8] { // # Safety // // - `buf[.. count]` has been initialized - <[MaybeUninit]>::assume_init_by_mut(&mut buf[.. count]) + buf.idx(.. count).unwrap().assume_init() }) } } @@ -95,7 +90,7 @@ macro_rules! impl_ReadIntoUninit_for_impl_BufRead {( #[inline] fn read_into_uninit<'buf> ( self: &'_ mut Self, - buf: &'buf mut [MaybeUninit], + buf: OutSlice<'buf, u8>, ) -> io::Result<&'buf mut [u8]> { let buf = { @@ -138,7 +133,7 @@ impl ReadIntoUninit for Box { #[inline] fn read_into_uninit<'buf> ( self: &'_ mut Self, - buf: &'buf mut [MaybeUninit], + buf: OutSlice<'buf, u8>, ) -> io::Result<&'buf mut [u8]> { (**self).read_into_uninit(buf) diff --git a/src/read/mod.rs b/src/read/mod.rs new file mode 100644 index 0000000..664e58b --- /dev/null +++ b/src/read/mod.rs @@ -0,0 +1,246 @@ +use_prelude!(); + +use ::std::io::Read; + +/// Trait for a [`Read`](https://doc.rust-lang.org/std/io/trait.Read.html)able +/// type that can output the bytes read into +/// [uninitialised memory][`MaybeUninit`]. +/// +/// # Safety +/// +/// The trait is marked `unsafe` because it **needs to guarantee** that: +/// +/// - `if let Ok(init_buf) = self.read_into_uninit(buf)`, then +/// `init_buf` is a prefix slice of `buf`. +/// +/// - this property is equivalent to: +/// +/// `init_buf.as_mut_ptr() == buf.as_mut_ptr() as *mut u8` and +/// `init_buf.len() <= buf.len()` +/// +/// - as well as: +/// +/// **`buf[.. init_buf.len()]` is sound to `assume_init`** +/// +/// `unsafe` code can assume this property to skip checks or manual +/// initialization, and that's why incorrectly `impl`-ementing this marker +/// trait can compromise memory safety. +pub +unsafe // Safety: `.read_into_uninit_exact()` delegates to `.read_into_uninit()`. +trait ReadIntoUninit : Read { + /// Single attempt to read bytes from `Self` into `buf`. + /// + /// On success, it returns the bytes having been read. + /// **This returned slice is guaranteed to be a prefix slice of `buf`**. + /// + /// This is not guaranteed to read `buf.len()` bytes, see the docs of + /// [`.read()`][`Read::read`] for more information. + fn read_into_uninit<'buf> ( + self: &'_ mut Self, + buf: OutSlice<'buf, u8>, + ) -> io::Result<&'buf mut [u8]> + ; + + /// Attempts to _fill_ `buf` through multiple `.read()` calls if necessary. + /// + /// On success, it returns the bytes having been read. + /// **This returned slice is guaranteed to be `buf`**. + /// + /// See the docs of [`.read_exact()`][`Read::read_exact`] for more + /// information. + fn read_into_uninit_exact<'buf> ( + self: &'_ mut Self, + mut buf: OutSlice<'buf, u8>, + ) -> io::Result<&'buf mut [u8]> + { + { + let mut buf = buf.reborrow(); + while buf.is_empty().not() { + match self + .read_into_uninit(buf.r()) + .map(|it| it.len()) + { + | Ok(0) => { + return Err(io::Error::new( + io::ErrorKind::UnexpectedEof, + "failed to fill whole buffer", + )); + }, + | Ok(n) => { + // buf = &mut buf[n ..]; + buf = buf.idx(n ..).unwrap(); + }, + | Err(ref e) + if e.kind() == io::ErrorKind::Interrupted + => {}, + | Err(e) => { + return Err(e); + }, + } + } + } + Ok(unsafe { + // # Safety + // + // - this is the "concatenation" of all the "buf[.. n]" + // initialisation witnesses. + buf.assume_init() + }) + } + + #[cfg(feature = "chain")] + fn chain ( + self: Self, + next: R, + ) -> chain::Chain + where + Self : Sized, + { + chain::Chain { first: self, second: next, first_done: false } + } + +} + +#[allow(unused_macros)] +macro_rules! auto_impl {( + #[derived_from(ReadIntoUninit)] + impl [$($generics:tt)*] io::Read for $T:ty + where + $($where_clause:tt)* +) => ( + impl<$($generics)*> io::Read for $T + where + $($where_clause)* + { + #[inline] + fn read (self: &'_ mut Self, buf: &'_ mut [u8]) + -> io::Result + { + ::read_into_uninit( + self, + buf.as_out_slice(), + ).map(|x| x.len()) + } + } +)} + +#[cfg(feature = "specialization")] +#[doc(cfg(feature = "specialization"))] +default +unsafe impl ReadIntoUninit for R { + default + fn read_into_uninit<'buf> ( + self: &'_ mut Self, + buf: OutSlice<'buf, u8>, + ) -> io::Result<&'buf mut [u8]> + { + let buf = buf.fill_with(|_| 0); + self.read(buf).map(move |n| &mut buf[.. n]) + } + + #[inline] + default + fn read_into_uninit_exact<'buf> ( + self: &'_ mut Self, + buf: OutSlice<'buf, u8>, + ) -> io::Result<&'buf mut [u8]> + { + let buf = buf.fill_with(|_| 0); + self.read_exact(buf).map(|()| buf) + } +} + +pub use crate::extension_traits::VecExtendFromReader; + +mod impls; + +#[cfg(feature = "chain")] +mod chain { + use super::*; + + #[derive(Debug)] + pub + struct Chain + where + R1 : ReadIntoUninit, + R2 : ReadIntoUninit, + { + pub(in super) + first: R1, + + pub(in super) + second: R2, + + pub(in super) + first_done: bool, + } + + impl Chain + where + R1 : ReadIntoUninit, + R2 : ReadIntoUninit, + { + pub + fn into_inner (self: Self) + -> (R1, R2) + { + let Self { first, second, ..} = self; + (first, second) + } + + pub + fn get_ref (self: &'_ Self) + -> (&'_ R1, &'_ R2) + { + let Self { first, second, ..} = self; + (first, second) + } + } + + unsafe + impl ReadIntoUninit for Chain + where + R1 : ReadIntoUninit, + R2 : ReadIntoUninit, + { + fn read_into_uninit<'buf> ( + self: &'_ mut Self, + mut buf: OutSlice<'buf, u8>, + ) -> io::Result<&'buf mut [u8]> + { + let len = buf.len(); + if len == 0 { + return Ok(unsafe { + // # Safety + // + // - since it has `0` elements, + // it does have `0` initialized elements. + buf.assume_init() + }) + } + if self.first_done.not() { + let buf_ = self.first.read_into_uninit(buf.r())?; + if buf_.is_empty() { + self.first_done = true; + } else { + return unsafe { + // Safety: `buf_` has been a witness of the + // initialization of these bytes. + let len = buf_.len(); + let buf = buf.idx(.. len).unwrap(); + Ok(buf.assume_init()) + }; + } + } + self.second.read_into_uninit(buf) + } + } + + auto_impl!( + #[derived_from(ReadIntoUninit)] + impl[R1, R2] io::Read for Chain + where + R1 : ReadIntoUninit, + R2 : ReadIntoUninit, + ); +} diff --git a/src/traits/mod.rs b/src/traits/mod.rs deleted file mode 100644 index 26d3a0e..0000000 --- a/src/traits/mod.rs +++ /dev/null @@ -1,10 +0,0 @@ -use super::*; - -pub use self::maybe_uninit::MaybeUninitExt; -mod maybe_uninit; - -pub -mod slice; - -pub -mod vec; diff --git a/src/traits/slice.rs b/src/traits/slice.rs deleted file mode 100644 index 2b75a64..0000000 --- a/src/traits/slice.rs +++ /dev/null @@ -1,93 +0,0 @@ -use super::*; -use ::core::ptr; - -/// Extension trait for `[`[`MaybeUninit`]`]`, that initializes the buffer -/// with a copy from another (already initialized) buffer. -/// -/// It returns a read-writable slice to the initialized bytes for convenience -/// (automatically -/// [`assume_init_by_mut`][`MaybeUninitExt::assume_init_by_mut`]-ed). -/// -/// # Panic -/// -/// The function panics if the slices' lengths are not equal. -/// -/// # Guarantees -/// -/// A non-`panic!`king return from this function **guarantees that the input -/// slice has been (successfully) initialized**, and it is thus then sound to -/// `.assume_init()`. -/// -/// It also guarantees that the returned slice does correspond to the input -/// slice (_e.g._, for [`ReadIntoUninit`]'s safety guarantees). -/// -/// # Example -/// -/// ```rust -/// # use ::core::mem::{self, MaybeUninit}; -/// use ::uninit::{InitWithCopyFromSlice, uninit_byte_array}; -/// -/// let mut array = uninit_byte_array![_; 13]; -/// assert_eq!( -/// array.init_with_copy_from_slice(b"Hello, World!"), -/// b"Hello, World!", -/// ); -/// // we can thus soundly `assume_init` our array: -/// let array = unsafe { -/// mem::transmute::< -/// [MaybeUninit; 13], -/// [ u8 ; 13], -/// >(array) -/// }; -/// assert_eq!( -/// array, -/// *b"Hello, World!", -/// ); -/// ``` -pub -trait InitWithCopyFromSlice { - type T; - - fn init_with_copy_from_slice<'out> ( - self: &'out mut Self, - source_slice: &'_ [Self::T], - ) -> &'out mut [Self::T] - ; -} - -impl InitWithCopyFromSlice for [MaybeUninit] { - type T = T; - - fn init_with_copy_from_slice<'out> ( - self: &'out mut [MaybeUninit], - source_slice: &'_ [T], - ) -> &'out mut [T] - { - assert_eq!( - self.len(), - source_slice.len(), - "`init_with_copy_from_slice()`: length mismatch", - ); - unsafe { - // # Safety - // - // - `&mut` guarantees no overlap; - // - // - `self[.. len]` is valid to write to; - // - // - `source_slice[.. len]` is valid to read (and copy) from. - ptr::copy_nonoverlapping( - source_slice.as_ptr(), - utils::ptr_cast_mut::, T>(self.as_mut_ptr()), - self.len(), - ); - } - unsafe { - // # Safety - // - // - the `copy_nonoverlapping()` call guarantees that the buffer - // has been initialized - <[MaybeUninit]>::assume_init_by_mut(self) - } - } -} diff --git a/src/utils/macros.rs b/src/utils/macros.rs index 919743d..261317d 100644 --- a/src/utils/macros.rs +++ b/src/utils/macros.rs @@ -14,5 +14,5 @@ macro_rules! pub_in_crate {( )} macro_rules! use_prelude {() => ( - pub(in crate) use crate::prelude::*; + pub(in crate) use crate::utils::prelude::*; )} diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 2c86e40..9a1ccaf 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1,6 +1,9 @@ #[macro_use] mod macros; +pub(in crate) +mod prelude; + #[inline] pub(in crate) fn ptr_cast_mut (p: *mut T) -> *mut U diff --git a/src/utils/prelude.rs b/src/utils/prelude.rs index cc57a35..c20e5b5 100644 --- a/src/utils/prelude.rs +++ b/src/utils/prelude.rs @@ -14,6 +14,12 @@ pub_in_crate! { }, }; use crate::{ - traits::*, + prelude::*, + extension_traits::{ + VecExtendFromReader, + }, + read::{ + ReadIntoUninit, + }, }; } From e3926f9f7c0e443bde47bff357ad1bbfd5eb97f5 Mon Sep 17 00:00:00 2001 From: Daniel Henry-Mantilla Date: Fri, 7 Feb 2020 21:07:10 +0100 Subject: [PATCH 2/5] Various improvements as suggested by @HeroicKatora It features: - enhancing `Vec::reserve_uninit` to a more general API for accessing with the backing buffer / capacity of a Vec; - Rename `idx` to `.get_out` - (Ab)use `.copy_from_slice()` in read implementations to use less unsafe TODO: - The iterator API, - Documentation Co-Authored-By: Andreas Molzer --- .cargo/config | 2 + Cargo.toml | 4 + src/extension_traits/as_out.rs | 81 +++++++-- src/extension_traits/boxed.rs | 160 +++++++++++++++++ src/extension_traits/manually_drop_mut.rs | 2 +- src/extension_traits/mod.rs | 7 +- src/extension_traits/vec.rs | 202 ++++++++++++++++------ src/lib.rs | 10 +- src/out_references.rs | 32 +--- src/read/impls.rs | 32 +--- src/read/mod.rs | 12 +- src/utils/mod.rs | 7 - 12 files changed, 417 insertions(+), 134 deletions(-) create mode 100644 .cargo/config create mode 100644 src/extension_traits/boxed.rs diff --git a/.cargo/config b/.cargo/config new file mode 100644 index 0000000..0b82483 --- /dev/null +++ b/.cargo/config @@ -0,0 +1,2 @@ +[build] +rustdocflags = ["-C", "opt-level=2"] diff --git a/Cargo.toml b/Cargo.toml index 4ac5848..12e18b8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,6 @@ +[lib] +crate-type = ["rlib", "cdylib"] + [package] name = "uninit" version = "0.1.0" @@ -20,6 +23,7 @@ require_unsafe_in_body = "0.2.0" [features] nightly = [] specialization = ["nightly"] +const_generics = ["nightly"] chain = [] default = [] diff --git a/src/extension_traits/as_out.rs b/src/extension_traits/as_out.rs index f270682..265fa23 100644 --- a/src/extension_traits/as_out.rs +++ b/src/extension_traits/as_out.rs @@ -11,7 +11,7 @@ mod private { impl Is for T { type Eq = T; } } -/// Helper / extension trait to convert a `&mut _` into a `&out T` by calling +/// Extension trait to convert a `&mut _` into a `&out T` by calling /// `.as_out::()` on it. /// /// By autoref, this means that you can even just extract a `&out T` reference @@ -129,8 +129,55 @@ impl<'out, T : 'out> AsOut<[T]> for &'out mut [ManuallyDrop] { } } -macro_rules! impl_arrays {( $($N:tt)* ) => ($( - impl<'out, T : 'out> AsOut<[T]> for &'out mut [MaybeUninit; $N] { +#[cfg(not(feature = "const_generics"))] +const _: () = { + macro_rules! impl_arrays {( $($N:tt)* ) => ($( + impl<'out, T : 'out> AsOut<[T]> for &'out mut [MaybeUninit; $N] { + type Out = OutSlice<'out, T>; + + #[inline] + fn as_out> (self: Self) + -> OutSlice<'out, T> + { + From::from(&mut self[..]) + } + } + impl<'out, T : 'out> AsOut<[T]> for &'out mut [T; $N] + where + T : Copy, + { + type Out = OutSlice<'out, T>; + + #[inline] + fn as_out> (self: Self) + -> OutSlice<'out, T> + { + From::from(&mut self[..]) + } + } + )*)} + + impl_arrays! { + 0 1 2 3 4 5 6 7 + 8 9 10 11 12 13 14 15 + 16 17 18 19 20 21 22 23 + 24 25 26 27 28 29 30 31 + 32 33 34 35 36 37 38 39 + 40 41 42 43 44 45 46 47 + 48 49 50 51 52 53 54 55 + 56 57 58 59 60 61 62 63 + 64 + 128 + 256 + 512 + 1024 + } +}; + +#[cfg(feature = "const_generics")] +const _: () = { + #[doc(cfg(feature = "const_generics"))] + impl<'out, T : 'out, const N: usize> AsOut<[T]> for &'out mut [MaybeUninit; N] { type Out = OutSlice<'out, T>; #[inline] @@ -140,16 +187,18 @@ macro_rules! impl_arrays {( $($N:tt)* ) => ($( From::from(&mut self[..]) } } -)*)} - -impl_arrays! { - 0 1 2 3 4 5 6 7 8 - 9 10 11 12 13 14 15 16 - 17 18 19 20 21 22 23 24 - 25 26 27 28 29 30 31 32 - 33 34 35 36 37 38 39 40 - 41 42 43 44 45 46 47 48 - 49 50 51 52 53 54 55 56 - 57 58 59 60 61 62 63 64 - 128 256 512 1024 -} + #[doc(cfg(feature = "const_generics"))] + impl<'out, T : 'out, const N: usize> AsOut<[T]> for &'out mut [T; N] + where + T : Copy, + { + type Out = OutSlice<'out, T>; + + #[inline] + fn as_out> (self: Self) + -> OutSlice<'out, T> + { + From::from(&mut self[..]) + } + } +}; diff --git a/src/extension_traits/boxed.rs b/src/extension_traits/boxed.rs new file mode 100644 index 0000000..7e142fd --- /dev/null +++ b/src/extension_traits/boxed.rs @@ -0,0 +1,160 @@ +use_prelude!(); + +use self::private::Sealed; +mod private { pub trait Sealed : Sized {} } +impl Sealed for Box> {} + +/// Extension trait for uninitalized `Box` allocations and +/// the optimized delayed-initialization pattern. +/// +/// # Optimized in-place heap initialization +/// +/// The `Box::uninit().init(...)` delayed-initialization pattern is suprisingly +/// effective in helping the optimizer inline the creation of the value directly +/// into the heap. +/// +/// - In other words, this bundles [`::copyless`](https://docs.rs/copyless) +/// functionality. +/// +/// - For those wondering why `Box::new(...)` could not be made as efficient, +/// the answer lies in temporaries: the `...` temporary when calling +/// `Box::new()` is created _before_ attempting the allocation, and given +/// that this allocation can fail / have side-effects, the optimizer is not +/// allowed to reorder the creation of the temporary after the allocation, +/// since it can change the semantics of the code for these corner (but +/// not unreachable) cases. It is hence illegal for the optimizer to inline +/// the creation of `...` directly into the heap. +/// +/// Whereas `Box::uninit().init(...)` only creates the temporary after +/// the allocation attempted in `uninit()` has succeeded, at which point +/// it should be trivial for the optimizer to inline its creation directly +/// into the heap. +/// +/// - Note, however, that this property cannot be guaranteed from a library +/// perspective; for instance, **the heap-inlined initialization does not +/// seem to happen when the optimization level (`opt-level`) is less than +/// `2`. Inversely, the author has observed that the heap-inlined +/// initialization does seem to kick in when compiling with `-C +/// opt-level=2`** (or `3`), _e.g._, when running on `--release`. +/// +/// +/// ### Example +/// +/// ```rust +/// use ::uninit::prelude::*; +/// +/// let ft: Box = Box::uninit().init(42); +/// assert_eq!(*ft, 42); +/// ``` +/// +/// This optimization can even allow creating arrays too big to fit in the +/// stack. +/// +/// - For instance, the following implementation panics: +/// +/// ```rust,should_panic +/// fn alloc_big_boxed_array () -> Box<[u64; 10_000_000]> +/// { +/// // This can panic because of the `[0; 10_000_000]` stack +/// // temporary overflowing the stack. +/// Box::new([0; 10_000_000]) +/// } +/// # println!("Address: {:p}", alloc_big_boxed_array()); +/// ``` +/// +/// - Whereas the following one does not +/// (doc-tested with `RUSTDOCFLAGS=-Copt-level=2`): +/// +/// ```rust +/// # use ::uninit::prelude::*; +/// fn alloc_big_boxed_array () -> Box<[u64; 10_000_000]> +/// { +/// // But this works fine, since there is no stack temporary! +/// Box::uninit().init([0; 10_000_000]) +/// } +/// # println!("Address: {:p}", alloc_big_boxed_array()); +/// ``` +/// +/// # Handling allocation failure +/// +/// A neat side-effect of this implementation is to expose the intermediate +/// state of `Box::try_alloc()`, which yields an `Option>>` +/// depending on whether the attempted allocation succeeded or not. +/// +/// ### Example +/// +/// ```rust,no_run +/// use ::uninit::prelude::*; +/// +/// let buf: Box<[u8; ::core::i32::MAX as _]> = match Box::try_alloc() { +/// | Some(uninit) => uninit.init([0; ::core::i32::MAX as _]), +/// | None => { +/// panic!("Failed to allocate 2GB of memory"); +/// } +/// }; +/// # let _ = buf; +/// ``` +impl BoxUninit for Box> { + type T = T; + + /// Idiomatic allocation-failure unwrapping of [`BoxUninit::try_alloc`]`()`. + #[inline] + fn uninit () + -> Box> + { + let layout = alloc::Layout::new::(); + if let Some(it) = Self::try_alloc() { it } else { + alloc::handle_alloc_error(layout); + } + } + + /// Attempts to `Box`-allocate memory for `T`, without initializing it. + /// + /// Returns `None` when the allocation fails. + #[inline] + fn try_alloc () + -> Option>> + {Some({ + if ::core::mem::size_of::() == 0 { + Box::new(MaybeUninit::uninit()) + } else { + unsafe { + let layout = alloc::Layout::new::(); + Self::from_raw( + ptr::NonNull::::new(alloc::alloc(layout).cast())? + .as_ptr() + .cast() + + ) + } + } + })} + + /// Safely initialize a `Box::MaybeUninit` by providing a `value: T` + /// (that can be inlined into the `Box`), and safely return the ergonomic + /// `Box` witness of that initialization. + #[inline(always)] + fn init (mut self: Box>, value: T) + -> Box + { + unsafe { + self.as_mut_ptr().write(value); + Box::from_raw(Box::into_raw(self).cast()) + } + } +} +/// Extension trait for uninitalized `Box` allocations and +/// the optimized delayed-initialization pattern. +pub +trait BoxUninit : Sealed { + type T; + fn uninit () + -> Self + ; + fn try_alloc () + -> Option + ; + fn init (self, value: Self::T) + -> Box + ; +} diff --git a/src/extension_traits/manually_drop_mut.rs b/src/extension_traits/manually_drop_mut.rs index 76bf215..c2dd77a 100644 --- a/src/extension_traits/manually_drop_mut.rs +++ b/src/extension_traits/manually_drop_mut.rs @@ -2,7 +2,7 @@ use ::core::mem::ManuallyDrop; #[cfg(doc)] use crate::Out; -/// An extension trait providing a cast to the [`ManuallyDrop`] type. +/// Extension trait providing a cast to the [`ManuallyDrop`] type. /// /// This is useful if you want to use an [`Out`] reference to something that /// is not `Copy` (potentially because it has drop glue, in which case you diff --git a/src/extension_traits/mod.rs b/src/extension_traits/mod.rs index aee5ae1..6d8dcb5 100644 --- a/src/extension_traits/mod.rs +++ b/src/extension_traits/mod.rs @@ -3,6 +3,11 @@ pub use self::as_out::{ }; mod as_out; +pub use self::boxed::{ + BoxUninit, +}; +mod boxed; + pub use self::manually_drop_mut::{ ManuallyDropMut, }; @@ -15,6 +20,6 @@ mod maybe_uninit; pub use self::vec::{ VecExtendFromReader, - VecReserveUninit, + VecAllocation, }; mod vec; diff --git a/src/extension_traits/vec.rs b/src/extension_traits/vec.rs index ef4592d..11d8fef 100644 --- a/src/extension_traits/vec.rs +++ b/src/extension_traits/vec.rs @@ -1,70 +1,174 @@ use crate::*; use ::core::slice; -/// Extension trait for [`Vec`], that reserves extra uninitialized memory for -/// it, and **returns a mutable handle on those extra (uninitialized) elements**. -/// -/// # Example -/// -/// ```rust -/// # use ::core::mem::MaybeUninit; -/// use ::uninit::prelude::*; -/// -/// let mut vec = b"Hello, ".to_vec(); -/// const WORLD: &[u8] = b"World!"; -/// -/// let extra: &mut [MaybeUninit] = vec.reserve_uninit(WORLD.len()); -/// extra.as_out::<[u8]>().copy_from_slice(WORLD); -/// -/// // `.reserve_uninit()` guarantees the following properties: -/// assert_eq!(extra.len(), WORLD.len()); -/// let extra_start: *mut u8 = extra.as_mut_ptr().cast(); -/// let uninit_start: *mut u8 = vec.as_mut_ptr().wrapping_add(vec.len()); -/// assert_eq!(extra_start, uninit_start); -/// -/// unsafe { -/// // # Safety -/// // -/// // - `.copy_from_slice()` contract guarantees initialization -/// // of `extra`, which, in turn, from `reserve_uninit`'s contract, -/// // leads to the `vec` extra capacity having been initialized. -/// vec.set_len(vec.len() + WORLD.len()); -/// } -/// assert_eq!( -/// vec, -/// b"Hello, World!", -/// ); -/// ``` +/// Extension trait for [`Vec`], allowing a non-`unsafe` API to interact +/// with the backing buffer / allocation. pub -trait VecReserveUninit { +trait VecAllocation : Sealed { type Item; - + fn get_backing_buffer (self: &'_ mut Self) + -> OutSlice<'_, Self::Item> + ; + fn into_backing_buffer (self: Self) + -> Vec> // ideally this would be a `Box<[]>`, but `Vec` does not guarantee it. + ; + fn split_at_extra_capacity (self: &'_ mut Self) + -> (&'_ mut [Self::Item], &'_ mut [MaybeUninit]) + ; fn reserve_uninit (self: &'_ mut Self, additional: usize) - -> &'_ mut [MaybeUninit] + -> &'_ mut [MaybeUninit] ; } -impl VecReserveUninit for Vec { + +mod private { pub trait Sealed : Sized {} } use private::Sealed; + +impl Sealed for Vec {} +impl VecAllocation for Vec +where + T : Copy, // Opinionated position against accidental memory leaks +{ type Item = T; - fn reserve_uninit (self: &'_ mut Self, additional: usize) - -> &'_ mut [MaybeUninit] + /// Gets an [`&out` slice][`OutSlice`] (of `self.capacity()` elements) to the backing buffer. + fn get_backing_buffer (self: &'_ mut Vec) + -> OutSlice<'_, T> { - self.reserve(additional); + let capacity = self.capacity(); unsafe { // # Safety // - // - Vec contract guarantees that after a call to `.reserve(n)` - // at least `n` uninitialized elements after the end of the - // Vec's current length can be soundly written to. + // - `Vec` safety invariant / layout guarantees state that + // it owns a `Box<[MaybeUninit]` of length `self.capacity()` + // and starting at `self.as_mut_ptr()`. slice::from_raw_parts_mut( - utils::ptr_cast_mut::>( - self.as_mut_ptr() - .wrapping_add(self.len()) + self.as_mut_ptr().cast::>(), + capacity, + ).as_out::<[Self::Item]>() + } + } + + /// Extracts an owned handle to the backing buffer. + /// + /// Ideally the return type would be a `Box<[MaybeUninit]>`, but AFAIK, + /// it is not officially guaranteed that the allocation of a `Vec` + /// (a `RawVec`) matches the allocation of a boxed slice, even if in their + /// current implementations they do. + fn into_backing_buffer (self: Vec) + -> Vec> // ideally this would be a `Box<[]>`, but `Vec` does not guarantee it. + { + let mut this = mem::ManuallyDrop::new(self); + let capacity = this.capacity(); + unsafe { + // Safety: + // + // - same layout; + // + // - `MaybeUninit>` is sound to `.assume_init()`; + // + // - init -> uninit conversion is sound by "covariance of ownership". + Vec::from_raw_parts( + this.as_mut_ptr().cast::>(), + capacity, + capacity, + ) + } + } + + /// Splits the `Vec`'s [backing buffer] into two slices of initialized and + /// unitialized elements. + /// + /// Imagine this as doing + /// `self.get_backing_buffer().split_at_out(self.len())` + /// while upgrading the first half to `&mut [T]` and the second half to a + /// `&mut [MaybeUninit]`. + /// + /// [backing buffer]: `VecAllocation::get_backing_buffer` + fn split_at_extra_capacity (self: &'_ mut Vec) + -> (&'_ mut [T], &'_ mut [MaybeUninit]) + { + let (ptr, len, cap) = { + let len = self.len(); + let cap = self.capacity(); + let ptr = self.as_mut_ptr().cast::>(); + (ptr, len, cap) + }; + unsafe { + // # Safety + // + // - `ptr[.. cap]` can be `&mut`-ed ("backing buffer"); + // + // - ptr[.. len] and ptr[len .. cap] do not overlap and thus do + // not alias; + // + // - ptr[.. len] is made of initialized elements; + // + // - ptr[len .. cap] is made of uninitialized elements; + // + // - the "optimized indexing" guarantees of `Vec` make the usage + // of `.add()` indexing sound (`RawVec` guards against the + // `mem::size_of::() * cap > isize::MAX as usize` overflow). + // + // - `cap >= len` is a safety invariant of `Vec`. + ( + slice::from_raw_parts_mut( + ptr.cast::(), + len, + ), + slice::from_raw_parts_mut( + ptr.add(len), + cap .checked_sub(len) + .unwrap_or_else(|| hint::unreachable_unchecked()) + , ), - additional, ) } } + + /// [Reserves][`Vec::reserve`] extra (uninitialized) memory for it, + /// **returning a mutable handle to those extra (uninitialized) elements**. + /// + /// # Example + /// + /// ```rust + /// # use ::core::mem::MaybeUninit; + /// use ::uninit::prelude::*; + /// + /// let mut vec = b"Hello, ".to_vec(); + /// const WORLD: &[u8] = b"World!"; + /// + /// let extra: &mut [MaybeUninit] = vec.reserve_uninit(WORLD.len()); + /// extra.as_out::<[u8]>().copy_from_slice(WORLD); + /// + /// // `.reserve_uninit()` guarantees the following properties: + /// assert_eq!(extra.len(), WORLD.len()); + /// let extra_start: *mut u8 = extra.as_mut_ptr().cast(); + /// let uninit_start: *mut u8 = vec.as_mut_ptr().wrapping_add(vec.len()); + /// assert_eq!(extra_start, uninit_start); + /// + /// unsafe { + /// // # Safety + /// // + /// // - `.copy_from_slice()` contract guarantees initialization + /// // of `extra`, which, in turn, from `reserve_uninit`'s contract, + /// // leads to the `vec` extra capacity having been initialized. + /// vec.set_len(vec.len() + WORLD.len()); + /// } + /// assert_eq!( + /// vec, + /// b"Hello, World!", + /// ); + /// ``` + fn reserve_uninit (self: &'_ mut Vec, additional: usize) + -> &'_ mut [MaybeUninit] + { + self.reserve(additional); + let (_, extra) = self.split_at_extra_capacity(); + unsafe { + // Safety: `Vec` guarantees that `cap >= len + additional` and + // thus that `cap - len >= additional`. + extra.get_unchecked_mut(.. additional) + } + } } /// Extension trait for [`Vec`], that grows the vec by a _bounded_ amount of @@ -104,7 +208,7 @@ impl VecExtendFromReader for Vec { mut reader: R, ) -> io::Result<()> { - let buf: OutSlice = self.reserve_uninit(count).into(); + let buf: OutSlice<'_, u8> = self.reserve_uninit(count).into(); let buf: &mut [u8] = reader.read_into_uninit_exact(buf)?; let count: usize = buf.len(); debug_assert_eq!( diff --git a/src/lib.rs b/src/lib.rs index 87ad3ab..95ad769 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,6 +5,12 @@ #![cfg_attr(feature = "specialization", feature(specialization), )] +#![cfg_attr(feature = "const_generics", + feature(const_generics), +)] +#![deny( + elided_lifetimes_in_paths, +)] #[macro_use] extern crate require_unsafe_in_body; @@ -16,8 +22,9 @@ pub mod prelude { pub use crate::{ extension_traits::{ AsOut, + BoxUninit, ManuallyDropMut, - VecReserveUninit, + VecAllocation, }, out_references::{ Out, @@ -25,6 +32,7 @@ pub mod prelude { }, uninit_array, }; + pub use ::core::mem::MaybeUninit; } use_prelude!(); diff --git a/src/out_references.rs b/src/out_references.rs index 8cf26b3..48c9312 100644 --- a/src/out_references.rs +++ b/src/out_references.rs @@ -3,7 +3,6 @@ use ::core::{ ManuallyDrop, MaybeUninit, }, - ptr, slice, }; @@ -271,7 +270,7 @@ impl<'out, T : 'out> OutSlice<'out, T> { #[inline] pub - fn idx (self: OutSlice<'out, T>, idx: Index) + fn get_out (self: OutSlice<'out, T>, idx: Index) -> Option where Index : SliceIndex<'out, T>, @@ -379,38 +378,23 @@ impl<'out, T : 'out> OutSlice<'out, T> { /// ``` pub fn copy_from_slice ( - mut self: OutSlice<'out, T>, + self: OutSlice<'out, T>, source_slice: &'_ [T], ) -> &'out mut [T] where T : Copy, { - assert_eq!( - self.len(), - source_slice.len(), - "`copy_from_slice()`: length mismatch", - ); unsafe { // # Safety // - // - `T : Copy` - // - // - `OutSlice` is unaliased and thus guarantees no overlap; - // - // - `self[.. len]` is valid to write to; - // - // - `source_slice[.. len]` is valid to read (and copy) from. - ptr::copy_nonoverlapping( - source_slice.as_ptr(), - self.as_mut_ptr(), - self.len(), - ); - } - unsafe { - // # Safety + // - Writing to `self.0` is fine since `source_slice` only + // contains initialized elements. // // - the `copy_nonoverlapping()` call guarantees that the buffer // has been initialized. + self.0.copy_from_slice( + crate::extension_traits::MaybeUninitExt::from_ref(source_slice) + ); self.assume_init() } } @@ -429,7 +413,7 @@ impl<'out, T : 'out> OutSlice<'out, T> { { let len = self.len(); (0 .. len).for_each(|i| { - self.r().idx(i).unwrap().write(factory(i)); + self.r().get_out(i).unwrap().write(factory(i)); }); unsafe { // Safety: The `len` values of the buffer have been initialized diff --git a/src/read/impls.rs b/src/read/impls.rs index 9dae785..16be8fb 100644 --- a/src/read/impls.rs +++ b/src/read/impls.rs @@ -26,42 +26,22 @@ unsafe impl ReadIntoUninit for &'_ [u8] { fn read_into_uninit<'buf> ( self: &'_ mut Self, - mut buf: OutSlice<'buf, u8>, + buf: OutSlice<'buf, u8>, ) -> io::Result<&'buf mut [u8]> { let count = ::std::cmp::min(buf.len(), self.len()); let (to_copy, remaining) = self.split_at(count); *self = remaining; - // First check if the amount of bytes we want to read is small: + // Taken from stdlib: + // "First check if the amount of bytes we want to read is small: // `copy_from_slice` will generally expand to a call to `memcpy`, and - // for a single byte the overhead is significant. + // for a single byte the overhead is significant." if count == 1 { - buf.reborrow().idx(0).unwrap().write(to_copy[0]); + Ok( slice::from_mut(buf.get_out(0).unwrap().write(to_copy[0])) ) } else { - unsafe { - // # Safety - // - // This is an unchecked version of `copy_from_slice`: - // - // - `to_copy[.. count]` is aligned and valid to read from, - // - // - `buf[.. count]` is aligned and valid to write to, - // - // - they cannot overlap given the `&mut` access on `buf` - ptr::copy_nonoverlapping::( - to_copy.as_ptr(), - buf.as_mut_ptr(), - count, - ); - } + Ok( buf.get_out(.. count).unwrap().copy_from_slice(to_copy) ) } - Ok(unsafe { - // # Safety - // - // - `buf[.. count]` has been initialized - buf.idx(.. count).unwrap().assume_init() - }) } } diff --git a/src/read/mod.rs b/src/read/mod.rs index 664e58b..b049dde 100644 --- a/src/read/mod.rs +++ b/src/read/mod.rs @@ -68,7 +68,7 @@ trait ReadIntoUninit : Read { }, | Ok(n) => { // buf = &mut buf[n ..]; - buf = buf.idx(n ..).unwrap(); + buf = buf.get_out(n ..).unwrap(); }, | Err(ref e) if e.kind() == io::ErrorKind::Interrupted @@ -210,13 +210,7 @@ mod chain { { let len = buf.len(); if len == 0 { - return Ok(unsafe { - // # Safety - // - // - since it has `0` elements, - // it does have `0` initialized elements. - buf.assume_init() - }) + return Ok(self.copy_from_slice(&[])); } if self.first_done.not() { let buf_ = self.first.read_into_uninit(buf.r())?; @@ -227,7 +221,7 @@ mod chain { // Safety: `buf_` has been a witness of the // initialization of these bytes. let len = buf_.len(); - let buf = buf.idx(.. len).unwrap(); + let buf = buf.get_out(.. len).unwrap(); Ok(buf.assume_init()) }; } diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 9a1ccaf..dfef50c 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -3,10 +3,3 @@ mod macros; pub(in crate) mod prelude; - -#[inline] -pub(in crate) -fn ptr_cast_mut (p: *mut T) -> *mut U -{ - p as _ -} From ce8f8bca24b3dbc62ee7f3015c6ff7974dac88ff Mon Sep 17 00:00:00 2001 From: Daniel Henry-Mantilla Date: Tue, 11 Feb 2020 00:46:57 +0100 Subject: [PATCH 3/5] Improve docs, start implementing iteration logic --- README.md | 4 +- src/extension_traits/boxed.rs | 14 +- src/extension_traits/manually_drop_mut.rs | 2 + src/extension_traits/maybe_uninit.rs | 4 +- src/extension_traits/mod.rs | 2 +- src/extension_traits/vec.rs | 318 ++++++++++++++-------- src/lib.rs | 2 +- src/out_references.rs | 65 +++-- src/read/mod.rs | 4 +- 9 files changed, 277 insertions(+), 138 deletions(-) diff --git a/README.md b/README.md index 891e6cd..d5f729a 100644 --- a/README.md +++ b/README.md @@ -180,7 +180,7 @@ pub trait Read { } ``` -that is, there is no way to `.read()` into an unitialized buffer (it would +that is, there is no way to `.read()` into an uninitialized buffer (it would require an api taking either a `(*mut u8, usize)` pair, or, equivalently and by the way more ergonomically, a [`&out [u8]`][`crate::prelude::OutSlice`]). @@ -211,6 +211,6 @@ So, the objective of this crate is double: [`.assume_init_by_ref()`]: `crate::extension_traits::MaybeUninitExt::assume_init_by_ref` [`.assume_init_by_mut()`]: `crate::extension_traits::MaybeUninitExt::assume_init_by_mut` [`uninit_array!`]: `uninit_array` -[`Vec::reserve_uninit`]: `crate::extension_traits::VecReserveUninit::reserve_uninit` +[`Vec::reserve_uninit`]: `crate::extension_traits::VecCapacity::reserve_uninit` [`.init_with_copy_from_slice()`]: `crate::out_references::OutSlice::copy_from_slice` [`ReadIntoUninit`]: `crate::read::ReadIntoUninit` diff --git a/src/extension_traits/boxed.rs b/src/extension_traits/boxed.rs index 7e142fd..bede5bf 100644 --- a/src/extension_traits/boxed.rs +++ b/src/extension_traits/boxed.rs @@ -116,12 +116,13 @@ impl BoxUninit for Box> { -> Option>> {Some({ if ::core::mem::size_of::() == 0 { - Box::new(MaybeUninit::uninit()) + Self::new(MaybeUninit::uninit()) } else { unsafe { + // Safety: we have guarded against ZST let layout = alloc::Layout::new::(); Self::from_raw( - ptr::NonNull::::new(alloc::alloc(layout).cast())? + ptr::NonNull::new(alloc::alloc(layout))? .as_ptr() .cast() @@ -137,8 +138,9 @@ impl BoxUninit for Box> { fn init (mut self: Box>, value: T) -> Box { + *self = MaybeUninit::new(value); unsafe { - self.as_mut_ptr().write(value); + // Safety: `self` has just been initialized. Box::from_raw(Box::into_raw(self).cast()) } } @@ -151,10 +153,10 @@ trait BoxUninit : Sealed { fn uninit () -> Self ; - fn try_alloc () - -> Option - ; fn init (self, value: Self::T) -> Box ; + fn try_alloc () + -> Option + ; } diff --git a/src/extension_traits/manually_drop_mut.rs b/src/extension_traits/manually_drop_mut.rs index c2dd77a..54af8d8 100644 --- a/src/extension_traits/manually_drop_mut.rs +++ b/src/extension_traits/manually_drop_mut.rs @@ -9,6 +9,8 @@ use ::core::mem::ManuallyDrop; /// either don't mind leaking / skipping that drop glue, or you know you will /// be manually handling it). /// +/// ⚠️ **Misusage of this function can thus lead to memory leaks** ⚠️ +/// /// # Example /// /// ```rust,compile_fail diff --git a/src/extension_traits/maybe_uninit.rs b/src/extension_traits/maybe_uninit.rs index 104074d..91a659b 100644 --- a/src/extension_traits/maybe_uninit.rs +++ b/src/extension_traits/maybe_uninit.rs @@ -32,8 +32,8 @@ use_prelude!(); /// ``` /// /// The author of the crate did overlook that and offered such transformation -/// within a non-`unsafe` function, leading to an unsound function. And now, to -/// that version being yanked. +/// within a non-`unsafe` function, leading to an unsound function. Hence the +/// yanked versions of the crate. /// /// The correct way to do this now is through /// [the `&out` reference abstraction][`crate::out_references`]. diff --git a/src/extension_traits/mod.rs b/src/extension_traits/mod.rs index 6d8dcb5..a2c37f7 100644 --- a/src/extension_traits/mod.rs +++ b/src/extension_traits/mod.rs @@ -20,6 +20,6 @@ mod maybe_uninit; pub use self::vec::{ VecExtendFromReader, - VecAllocation, + VecCapacity, }; mod vec; diff --git a/src/extension_traits/vec.rs b/src/extension_traits/vec.rs index 11d8fef..b01a27e 100644 --- a/src/extension_traits/vec.rs +++ b/src/extension_traits/vec.rs @@ -4,124 +4,118 @@ use ::core::slice; /// Extension trait for [`Vec`], allowing a non-`unsafe` API to interact /// with the backing buffer / allocation. pub -trait VecAllocation : Sealed { +trait VecCapacity : Sealed { type Item; + + fn split_at_extra_cap (self: &'_ mut Self) + -> (&'_ mut [Self::Item], OutSlice<'_, Self::Item>) + ; + fn reserve_uninit (self: &'_ mut Self, additional: usize) + -> OutSlice<'_, Self::Item> + ; fn get_backing_buffer (self: &'_ mut Self) -> OutSlice<'_, Self::Item> + where + Self::Item : Copy, // Opinionated stance against accidental memory leaks ; fn into_backing_buffer (self: Self) - -> Vec> // ideally this would be a `Box<[]>`, but `Vec` does not guarantee it. + -> Box<[MaybeUninit]> + where + Self::Item : Copy, // Opinionated stance against accidental memory leaks ; - fn split_at_extra_capacity (self: &'_ mut Self) - -> (&'_ mut [Self::Item], &'_ mut [MaybeUninit]) + fn get_backing_buffer_manually_drop (self: &'_ mut Self) + -> OutSlice<'_, Self::Item> ; - fn reserve_uninit (self: &'_ mut Self, additional: usize) - -> &'_ mut [MaybeUninit] + fn into_backing_buffer_manually_drop (self: Self) + -> Box<[MaybeUninit]> ; } mod private { pub trait Sealed : Sized {} } use private::Sealed; impl Sealed for Vec {} -impl VecAllocation for Vec -where - T : Copy, // Opinionated position against accidental memory leaks -{ +impl VecCapacity for Vec { type Item = T; - /// Gets an [`&out` slice][`OutSlice`] (of `self.capacity()` elements) to the backing buffer. - fn get_backing_buffer (self: &'_ mut Vec) - -> OutSlice<'_, T> - { - let capacity = self.capacity(); - unsafe { - // # Safety - // - // - `Vec` safety invariant / layout guarantees state that - // it owns a `Box<[MaybeUninit]` of length `self.capacity()` - // and starting at `self.as_mut_ptr()`. - slice::from_raw_parts_mut( - self.as_mut_ptr().cast::>(), - capacity, - ).as_out::<[Self::Item]>() - } - } - - /// Extracts an owned handle to the backing buffer. - /// - /// Ideally the return type would be a `Box<[MaybeUninit]>`, but AFAIK, - /// it is not officially guaranteed that the allocation of a `Vec` - /// (a `RawVec`) matches the allocation of a boxed slice, even if in their - /// current implementations they do. - fn into_backing_buffer (self: Vec) - -> Vec> // ideally this would be a `Box<[]>`, but `Vec` does not guarantee it. - { - let mut this = mem::ManuallyDrop::new(self); - let capacity = this.capacity(); - unsafe { - // Safety: - // - // - same layout; - // - // - `MaybeUninit>` is sound to `.assume_init()`; - // - // - init -> uninit conversion is sound by "covariance of ownership". - Vec::from_raw_parts( - this.as_mut_ptr().cast::>(), - capacity, - capacity, - ) - } - } - - /// Splits the `Vec`'s [backing buffer] into two slices of initialized and - /// unitialized elements. + /// Splits the `Vec`'s + /// [backing buffer][`VecCapacity::get_backing_buffer`] into two slices of + /// initialized and uninitialized elements. /// /// Imagine this as doing /// `self.get_backing_buffer().split_at_out(self.len())` /// while upgrading the first half to `&mut [T]` and the second half to a /// `&mut [MaybeUninit]`. /// - /// [backing buffer]: `VecAllocation::get_backing_buffer` - fn split_at_extra_capacity (self: &'_ mut Vec) - -> (&'_ mut [T], &'_ mut [MaybeUninit]) + /// # Guarantees (that `unsafe` code may rely on): + /// + /// Given a vector `v`, and `let (xs, extra) = v.split_at_extra_cap()`, + /// then: + /// + /// - `xs` _is_ `v.as_slice()`, so: + /// + /// - `xs.len() == v.len()`, + /// + /// - `xs.as_ptr() == v.as_ptr()`, + /// + /// - `extra.len() == (v.capacity() - v.len())`; + /// + /// - if `.split_at_extra_cap()` is called right after a call to + /// `.reserve(n)`, then `v.capacity() ≥ v.len() + n`, and thus + /// **`extra.len() ≥ n`**. + /// + /// For the `extra.len() == n` equality to hold, one must subslice + /// `extra`: `extra = &mut extra[.. n];`. + /// And given the aformentioned guarantees, one can even: + /// `extra = extra.get_mut_unchecked(.. n);` + /// + /// This last idiom is covered by + /// [`.reserve_uninit(n)`][`VecCapacity::reserve_uninit`]. + /// + /// - `extra.as_ptr().cast() == v.as_ptr().add(v.len())`. + /// + /// - Thus, (only) after initializing the first `k` elements of `extra`, + /// it is sound to `v.set_len(v.len() + k);`. + /// + /// # Example + /// + /// Making a palindrome `Vec`: + /// + /// ```rust + /// use ::uninit::prelude::*; + /// + /// fn make_palindrome (v: &'_ mut Vec) + /// { + /// let len = v.len(); + /// v.reserve(len); + /// let (xs, extra) = v.split_at_extra_cap(); + /// for (&x, at_dst) in xs.iter().rev().zip(extra) { + /// at_dst.write(x); + /// } + /// unsafe { + /// // Safety: the first `len` elements of `extra` have been initialized. + /// v.set_len(2 * len); + /// } + /// } + /// + /// let mut v = vec![1, 2, 3]; + /// make_palindrome(&mut v); + /// assert_eq!(v, [1, 2, 3, 3, 2, 1]); + /// ``` + #[inline] + fn split_at_extra_cap (self: &'_ mut Vec) + -> (&'_ mut [T], OutSlice<'_, T>) { - let (ptr, len, cap) = { - let len = self.len(); - let cap = self.capacity(); - let ptr = self.as_mut_ptr().cast::>(); - (ptr, len, cap) - }; - unsafe { - // # Safety - // - // - `ptr[.. cap]` can be `&mut`-ed ("backing buffer"); - // - // - ptr[.. len] and ptr[len .. cap] do not overlap and thus do - // not alias; - // - // - ptr[.. len] is made of initialized elements; - // - // - ptr[len .. cap] is made of uninitialized elements; - // - // - the "optimized indexing" guarantees of `Vec` make the usage - // of `.add()` indexing sound (`RawVec` guards against the - // `mem::size_of::() * cap > isize::MAX as usize` overflow). - // - // - `cap >= len` is a safety invariant of `Vec`. - ( - slice::from_raw_parts_mut( - ptr.cast::(), - len, - ), - slice::from_raw_parts_mut( - ptr.add(len), - cap .checked_sub(len) - .unwrap_or_else(|| hint::unreachable_unchecked()) - , - ), - ) - } + let len = self.len(); + let backing_buffer = self.get_backing_buffer_manually_drop(); + let (mut xs, extra) = backing_buffer.split_at_out(len); + ( + unsafe { + // Safety: the first `len` elements are initialized (safety + // invariant of `Vec`). + slice::from_raw_parts_mut(xs.as_mut_ptr(), len) + }, + extra, + ) } /// [Reserves][`Vec::reserve`] extra (uninitialized) memory for it, @@ -136,12 +130,12 @@ where /// let mut vec = b"Hello, ".to_vec(); /// const WORLD: &[u8] = b"World!"; /// - /// let extra: &mut [MaybeUninit] = vec.reserve_uninit(WORLD.len()); - /// extra.as_out::<[u8]>().copy_from_slice(WORLD); + /// let mut extra: OutSlice = vec.reserve_uninit(WORLD.len()); + /// extra.r().copy_from_slice(WORLD); /// /// // `.reserve_uninit()` guarantees the following properties: /// assert_eq!(extra.len(), WORLD.len()); - /// let extra_start: *mut u8 = extra.as_mut_ptr().cast(); + /// let extra_start: *mut u8 = extra.r().as_mut_ptr().cast(); /// let uninit_start: *mut u8 = vec.as_mut_ptr().wrapping_add(vec.len()); /// assert_eq!(extra_start, uninit_start); /// @@ -158,17 +152,130 @@ where /// b"Hello, World!", /// ); /// ``` + #[inline] fn reserve_uninit (self: &'_ mut Vec, additional: usize) - -> &'_ mut [MaybeUninit] + -> OutSlice<'_, T> { self.reserve(additional); - let (_, extra) = self.split_at_extra_capacity(); + let (_, extra) = self.split_at_extra_cap(); unsafe { // Safety: `Vec` guarantees that `cap >= len + additional` and // thus that `cap - len >= additional`. - extra.get_unchecked_mut(.. additional) + extra.get_out(.. additional) + .unwrap_or_else(|| hint::unreachable_unchecked()) + } + } + + /// Gets an [`&out [T]` slice][`OutSlice`] (of `self.capacity()` elements) + /// to the backing buffer. + #[inline] + fn get_backing_buffer (self: &'_ mut Vec) + -> OutSlice<'_, T> + where + T : Copy, // Opinionated stance against accidental memory leaks + { + self.get_backing_buffer_manually_drop() + } + + /// Same as [`.get_backing_buffer()`][`VecCapacity::get_backing_buffer`] + /// but without the `Copy` bound. + /// + /// This means that extra care should be taken if + /// `mem::needs_drop::()`. Indeed, if the `OutSlice` is used to + /// overwrite initialized elements, then such elements will be destroyed + /// without their `.drop()` glue / destructors ever being run. + /// + /// ⚠️ **Misusage of this function can thus lead to memory leaks** ⚠️ + /// + /// # Counter-example + /// + /// ```rust + /// use ::uninit::prelude::*; + /// + /// let mut v = vec![Some(Box::new(42))]; + /// v .get_backing_buffer_manually_drop() + /// .get_out(0) + /// .unwrap() + /// .write(None) // the box is not freed despite now being unreachable. + /// ; + /// ``` + /// + /// # Example + /// + /// ```rust + /// use ::uninit::prelude::*; + /// use ::std::cell::Cell; + /// + /// let mut v = vec![Cell::new(0)]; + /// v .get_backing_buffer_manually_drop() // No drop glue, so this is fine + /// .get_out(0) + /// .unwrap() + /// .write(Cell::new(42)) + /// ; + /// assert_eq!(v[0].get(), 42); + /// ``` + #[inline] + fn get_backing_buffer_manually_drop (self: &'_ mut Vec) + -> OutSlice<'_, T> + { + let capacity = self.capacity(); + unsafe { + // # Safety + // + // - `Vec` safety invariant / layout guarantees state that + // it owns a `Box<[MaybeUninit]` of length `self.capacity()` + // and starting at `self.as_mut_ptr()`. + slice::from_raw_parts_mut( + self.as_mut_ptr().cast::>(), + capacity, + ).as_out::<[T]>() } } + + /// Extracts an owned handle to the backing buffer. + #[inline] + fn into_backing_buffer (self: Vec) + -> Box<[MaybeUninit]> + where + T : Copy, // Opinionated stance against accidental memory leaks + { + self.into_backing_buffer_manually_drop() + } + + /// Same as [`.into_backing_buffer()`][ + /// `VecCapacity::into_backing_buffer`] but without the `Copy` bound. + /// + /// This means that extra care should be taken if + /// `mem::needs_drop::()`. Indeed, the returned boxed slice + /// will not run the destructor of its initialized elements (since it no + /// longer knows which are). + /// + /// ⚠️ **Misusage of this function can thus lead to memory leaks** ⚠️ + #[inline] + fn into_backing_buffer_manually_drop (self: Vec) + -> Box<[MaybeUninit]> + { + // We need to go through an intermediate max-capacity `Vec` because + // there is no official guarantee that a `Vec`'s backing allocation + // matches that of a boxed slice. + let this: Vec> = unsafe { + // Safety: + // + // - same layout; + // + // - `MaybeUninit>` is sound to `.assume_init()`; + // + // - init -> uninit conversion is sound by "covariance of ownership". + let mut this = mem::ManuallyDrop::new(self); + let capacity = this.capacity(); + Vec::from_raw_parts( + this.as_mut_ptr().cast::>(), + capacity, + capacity, + ) + }; + this.into_boxed_slice() // hopefully a no-op + } } /// Extension trait for [`Vec`], that grows the vec by a _bounded_ amount of @@ -200,15 +307,15 @@ trait VecExtendFromReader { ; } -#[require_unsafe_in_bodies] impl VecExtendFromReader for Vec { + #[inline] fn extend_from_reader ( self: &'_ mut Self, count: usize, mut reader: R, ) -> io::Result<()> { - let buf: OutSlice<'_, u8> = self.reserve_uninit(count).into(); + let buf: OutSlice<'_, u8> = self.reserve_uninit(count); let buf: &mut [u8] = reader.read_into_uninit_exact(buf)?; let count: usize = buf.len(); debug_assert_eq!( @@ -232,8 +339,7 @@ impl VecExtendFromReader for Vec { // - `buf: &mut [u8]` has been a witness of the initialization of // the bytes in the `len .. len + count` range. self.set_len( - new_len - .unwrap_or_else(|| hint::unreachable_unchecked()) + new_len.unwrap_or_else(|| hint::unreachable_unchecked()) ); } Ok(()) diff --git a/src/lib.rs b/src/lib.rs index 95ad769..3aba909 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,7 +24,7 @@ pub mod prelude { AsOut, BoxUninit, ManuallyDropMut, - VecAllocation, + VecCapacity, }, out_references::{ Out, diff --git a/src/out_references.rs b/src/out_references.rs index 48c9312..e497132 100644 --- a/src/out_references.rs +++ b/src/out_references.rs @@ -411,34 +411,23 @@ impl<'out, T : 'out> OutSlice<'out, T> { mut factory: impl FnMut(usize) -> T, ) -> &'out mut [T] { - let len = self.len(); - (0 .. len).for_each(|i| { - self.r().get_out(i).unwrap().write(factory(i)); - }); + self.iter_out() + .enumerate() + .for_each(|(i, at_dst)| { at_dst.write(factory(i)); }) + ; unsafe { // Safety: The `len` values of the buffer have been initialized self.assume_init() } } + /// `.reborrow().into_iter()` #[inline] pub fn iter_out (self: &'_ mut OutSlice<'out, T>) - -> impl Iterator> + '_ + -> iter::IterOut<'_, T> { - ::core::iter::from_fn({ - let mut slice = &mut *self.0; - move || Some({ - if slice.is_empty() { return None; } - let (first, rest) = - mem::replace(&mut slice, &mut []) - .split_at_mut(1) - ; - let first: &mut MaybeUninit = &mut first[0]; - slice = rest; - first.into() - }) - }) + self.r().into_iter() } #[inline] @@ -477,3 +466,43 @@ mod private { ; } } + +pub +mod iter { + use super::*; + + pub + struct IterOut<'out, T : 'out> /* = */ ( + pub + OutSlice<'out, T>, + ); + + impl<'out, T : 'out> IntoIterator for OutSlice<'out, T> { + type Item = Out<'out, T>; + type IntoIter = IterOut<'out, T>; + + fn into_iter (self: OutSlice<'out, T>) + -> IterOut<'out, T> + { + IterOut(self) + } + } + + impl<'out, T : 'out> Iterator for IterOut<'out, T> { + type Item = Out<'out, T>; + + #[inline] + fn next (self: &'_ mut IterOut<'out, T>) + -> Option> + { + let this = &mut self.0; + if this.is_empty() { return None; } + let slice = mem::replace(this, <&mut [MaybeUninit]>::into(&mut [])); + let (first, rest) = slice.split_at_out(1); + *this = rest; + Some(first.get_out(0).unwrap_or_else(|| unsafe { + ::core::hint::unreachable_unchecked() + })) + } + } +} diff --git a/src/read/mod.rs b/src/read/mod.rs index b049dde..d27c1f4 100644 --- a/src/read/mod.rs +++ b/src/read/mod.rs @@ -182,7 +182,7 @@ mod chain { { pub fn into_inner (self: Self) - -> (R1, R2) + -> (R1, R2) { let Self { first, second, ..} = self; (first, second) @@ -190,7 +190,7 @@ mod chain { pub fn get_ref (self: &'_ Self) - -> (&'_ R1, &'_ R2) + -> (&'_ R1, &'_ R2) { let Self { first, second, ..} = self; (first, second) From 634fec7c5c6b97be953bc65f19b2d209d5ae2680 Mon Sep 17 00:00:00 2001 From: Daniel Henry-Mantilla Date: Mon, 24 Feb 2020 13:58:04 +0100 Subject: [PATCH 4/5] =?UTF-8?q?Unified=20`Out`=20and=20`OutSlice`=20into?= =?UTF-8?q?=20a=20single=20`Out=20type`=C3=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Not only does this lead to replacing `OutSlice` with the more readable `Out<[T]>`, it also results in other parts of the API being greatly simplified (mainly the `AsOut` trait). --- README.md | 12 +- src/extension_traits/as_out.rs | 103 ++---- src/extension_traits/manually_drop_mut.rs | 4 +- src/extension_traits/maybe_uninit.rs | 2 +- src/extension_traits/vec.rs | 27 +- src/lib.rs | 7 +- src/{out_references.rs => out_ref.rs} | 385 +++++++++++++--------- src/read/impls.rs | 8 +- src/read/mod.rs | 14 +- 9 files changed, 297 insertions(+), 265 deletions(-) rename src/{out_references.rs => out_ref.rs} (55%) diff --git a/README.md b/README.md index d5f729a..815e0c5 100644 --- a/README.md +++ b/README.md @@ -124,7 +124,7 @@ It is all about the _**delayed** initialization pattern_: unsafe { x.as_mut_ptr().write(42); - assert_eq!(42, x.assume_init()); + assert_eq!(x.assume_init(), 42); } ``` @@ -138,8 +138,8 @@ It is all about the _**delayed** initialization pattern_: use ::uninit::prelude::*; let mut x = MaybeUninit::uninit(); - let at_init_x: &i32 = x.as_out::().write(42); - assert_eq!(42, *at_init_x); + let at_init_x: &i32 = x.as_out().write(42); + assert_eq!(at_init_x, &42); ``` 3. **Type-level upgrade** @@ -182,7 +182,7 @@ pub trait Read { that is, there is no way to `.read()` into an uninitialized buffer (it would require an api taking either a `(*mut u8, usize)` pair, or, equivalently and -by the way more ergonomically, a [`&out [u8]`][`crate::prelude::OutSlice`]). +by the way more ergonomically, a [`&out [u8]`][`crate::prelude::Out`]). # Enter `::uninit` @@ -203,7 +203,7 @@ So, the objective of this crate is double: - [`ReadIntoUninit`] - - [`.init_with_copy_from_slice()`] + - [Initialize an uninitialized buffer with `.copy_from_slice()`] [`Read`]: https://doc.rust-lang.org/1.36.0/std/io/trait.Read.html [`mem::uninitialized`]: https://doc.rust-lang.org/core/mem/fn.uninitialized.html @@ -212,5 +212,5 @@ So, the objective of this crate is double: [`.assume_init_by_mut()`]: `crate::extension_traits::MaybeUninitExt::assume_init_by_mut` [`uninit_array!`]: `uninit_array` [`Vec::reserve_uninit`]: `crate::extension_traits::VecCapacity::reserve_uninit` -[`.init_with_copy_from_slice()`]: `crate::out_references::OutSlice::copy_from_slice` +[Initialize an uninitialized buffer with `.copy_from_slice()`]: `crate::out_ref::Out::copy_from_slice` [`ReadIntoUninit`]: `crate::read::ReadIntoUninit` diff --git a/src/extension_traits/as_out.rs b/src/extension_traits/as_out.rs index 265fa23..639e095 100644 --- a/src/extension_traits/as_out.rs +++ b/src/extension_traits/as_out.rs @@ -5,25 +5,16 @@ use ::core::mem::ManuallyDrop; #[cfg(doc)] use crate::extension_traits::ManuallyDropMut; -use private::Is; -mod private { - pub trait Is { type Eq : ?Sized; } - impl Is for T { type Eq = T; } -} - -/// Extension trait to convert a `&mut _` into a `&out T` by calling -/// `.as_out::()` on it. +/// Extension trait to convert a `&mut _` into a `&out ` by calling +/// `.as_out()` on it. /// /// By autoref, this means that you can even just extract a `&out T` reference -/// out of a `mut` element simply by calling `.as_out::()` on it. +/// out of a `mut` element simply by calling `.as_out()` on it. /// -/// There is, however, one restriction: to be able to call `.as_out::<_>()` on +/// There is, however, one restriction: to be able to call `.as_out()` on /// something, it needs to be either `Copy`, or a value wrapped in a /// [`MaybeUninit`] / a [`ManuallyDrop`]. /// -/// - (or a slice of such, in which case it yields a -/// [fat `&out` reference][`OutSlice`]) -/// /// This is by design. Indeed, [`Out`] references do not call the destructor /// of the overwritten element (since it may not be initialized). /// This could cause memory leaks when there is an initialized element with @@ -40,90 +31,76 @@ mod private { /// use ::uninit::prelude::*; /// /// let mut x = 0; -/// x.as_out::().write(42); +/// x.as_out().write(42); /// /// let mut y = ::core::mem::MaybeUninit::uninit(); -/// y.as_out::().write(42); +/// y.as_out().write(42); /// let y = unsafe { y.assume_init() }; /// /// assert_eq!(x, y); /// ``` pub -trait AsOut { - type Out; - - fn as_out> (self: Self) - -> Self::Out +trait AsOut { + fn as_out<'out> (self: &'out mut Self) + -> Out<'out, Pointee> ; } -impl<'out, T : 'out> AsOut for &'out mut MaybeUninit { - type Out = Out<'out, T>; - +impl AsOut for MaybeUninit { #[inline] - fn as_out> (self: &'out mut MaybeUninit) + fn as_out<'out> (self: &'out mut MaybeUninit) -> Out<'out, T> { self.into() } } -impl<'out, T : 'out> AsOut for &'out mut T +impl AsOut for T where T : Copy, { - type Out = Out<'out, T>; - #[inline] - fn as_out> (self: &'out mut T) + fn as_out<'out> (self: &'out mut T) -> Out<'out, T> { self.into() } } -impl<'out, T : 'out> AsOut<[T]> for &'out mut [MaybeUninit] { - type Out = OutSlice<'out, T>; - +impl AsOut<[T]> for [MaybeUninit] { #[inline] - fn as_out> (self: &'out mut [MaybeUninit]) - -> OutSlice<'out, T> + fn as_out<'out> (self: &'out mut [MaybeUninit]) + -> Out<'out, [T]> { self.into() } } -impl<'out, T : 'out> AsOut<[T]> for &'out mut [T] +impl AsOut<[T]> for [T] where T : Copy, { - type Out = OutSlice<'out, T>; - #[inline] - fn as_out> (self: &'out mut [T]) - -> OutSlice<'out, T> + fn as_out<'out> (self: &'out mut [T]) + -> Out<'out, [T]> { self.into() } } -impl<'out, T : 'out> AsOut for &'out mut ManuallyDrop { - type Out = Out<'out, T>; - +impl AsOut for ManuallyDrop { #[inline] - fn as_out> (self: &'out mut ManuallyDrop) + fn as_out<'out> (self: &'out mut ManuallyDrop) -> Out<'out, T> { self.into() } } -impl<'out, T : 'out> AsOut<[T]> for &'out mut [ManuallyDrop] { - type Out = OutSlice<'out, T>; - +impl AsOut<[T]> for [ManuallyDrop] { #[inline] - fn as_out> (self: &'out mut [ManuallyDrop]) - -> OutSlice<'out, T> + fn as_out<'out> (self: &'out mut [ManuallyDrop]) + -> Out<'out, [T]> { self.into() } @@ -132,25 +109,21 @@ impl<'out, T : 'out> AsOut<[T]> for &'out mut [ManuallyDrop] { #[cfg(not(feature = "const_generics"))] const _: () = { macro_rules! impl_arrays {( $($N:tt)* ) => ($( - impl<'out, T : 'out> AsOut<[T]> for &'out mut [MaybeUninit; $N] { - type Out = OutSlice<'out, T>; - + impl AsOut<[T]> for [MaybeUninit; $N] { #[inline] - fn as_out> (self: Self) - -> OutSlice<'out, T> + fn as_out<'out> (self: &'out mut Self) + -> Out<'out, [T]> { From::from(&mut self[..]) } } - impl<'out, T : 'out> AsOut<[T]> for &'out mut [T; $N] + impl AsOut<[T]> for [T; $N] where T : Copy, { - type Out = OutSlice<'out, T>; - #[inline] - fn as_out> (self: Self) - -> OutSlice<'out, T> + fn as_out<'out> (self: &'out mut Self) + -> Out<'out, [T]> { From::from(&mut self[..]) } @@ -177,26 +150,22 @@ const _: () = { #[cfg(feature = "const_generics")] const _: () = { #[doc(cfg(feature = "const_generics"))] - impl<'out, T : 'out, const N: usize> AsOut<[T]> for &'out mut [MaybeUninit; N] { - type Out = OutSlice<'out, T>; - + impl AsOut<[T]> for [MaybeUninit; N] { #[inline] - fn as_out> (self: Self) - -> OutSlice<'out, T> + fn as_out<'out> (self: &'out mut Self) + -> Out<'out, [T]> { From::from(&mut self[..]) } } #[doc(cfg(feature = "const_generics"))] - impl<'out, T : 'out, const N: usize> AsOut<[T]> for &'out mut [T; N] + impl AsOut<[T]> for [T; N] where T : Copy, { - type Out = OutSlice<'out, T>; - #[inline] - fn as_out> (self: Self) - -> OutSlice<'out, T> + fn as_out<'out> (self: &'out mut Self) + -> Out<'out, [T]> { From::from(&mut self[..]) } diff --git a/src/extension_traits/manually_drop_mut.rs b/src/extension_traits/manually_drop_mut.rs index 54af8d8..523d4e0 100644 --- a/src/extension_traits/manually_drop_mut.rs +++ b/src/extension_traits/manually_drop_mut.rs @@ -18,7 +18,7 @@ use ::core::mem::ManuallyDrop; /// use ::uninit::prelude::{AsOut, ManuallyDropMut}; /// /// let mut cell = Cell::new(0); -/// cell.as_out::>().write(Cell::new(42)); // Error, not `Copy` +/// cell.as_out().write(Cell::new(42)); // Error, not `Copy` /// assert_eq!(cell.get(), 42); /// ``` /// @@ -27,7 +27,7 @@ use ::core::mem::ManuallyDrop; /// use ::uninit::prelude::{AsOut, ManuallyDropMut}; /// /// let mut cell = Cell::new(0); -/// cell.manually_drop_mut().as_out::>().write(Cell::new(42)); // OK +/// cell.manually_drop_mut().as_out().write(Cell::new(42)); // OK /// assert_eq!(cell.get(), 42); /// ``` pub diff --git a/src/extension_traits/maybe_uninit.rs b/src/extension_traits/maybe_uninit.rs index 91a659b..1261a37 100644 --- a/src/extension_traits/maybe_uninit.rs +++ b/src/extension_traits/maybe_uninit.rs @@ -36,7 +36,7 @@ use_prelude!(); /// yanked versions of the crate. /// /// The correct way to do this now is through -/// [the `&out` reference abstraction][`crate::out_references`]. +/// [the `&out` reference abstraction][`crate::out_ref`]. pub trait MaybeUninitExt { type T : ?Sized; diff --git a/src/extension_traits/vec.rs b/src/extension_traits/vec.rs index b01a27e..630da24 100644 --- a/src/extension_traits/vec.rs +++ b/src/extension_traits/vec.rs @@ -8,13 +8,13 @@ trait VecCapacity : Sealed { type Item; fn split_at_extra_cap (self: &'_ mut Self) - -> (&'_ mut [Self::Item], OutSlice<'_, Self::Item>) + -> (&'_ mut [Self::Item], Out<'_, [Self::Item]>) ; fn reserve_uninit (self: &'_ mut Self, additional: usize) - -> OutSlice<'_, Self::Item> + -> Out<'_, [Self::Item]> ; fn get_backing_buffer (self: &'_ mut Self) - -> OutSlice<'_, Self::Item> + -> Out<'_, [Self::Item]> where Self::Item : Copy, // Opinionated stance against accidental memory leaks ; @@ -24,7 +24,7 @@ trait VecCapacity : Sealed { Self::Item : Copy, // Opinionated stance against accidental memory leaks ; fn get_backing_buffer_manually_drop (self: &'_ mut Self) - -> OutSlice<'_, Self::Item> + -> Out<'_, [Self::Item]> ; fn into_backing_buffer_manually_drop (self: Self) -> Box<[MaybeUninit]> @@ -103,7 +103,7 @@ impl VecCapacity for Vec { /// ``` #[inline] fn split_at_extra_cap (self: &'_ mut Vec) - -> (&'_ mut [T], OutSlice<'_, T>) + -> (&'_ mut [T], Out<'_, [T]>) { let len = self.len(); let backing_buffer = self.get_backing_buffer_manually_drop(); @@ -130,7 +130,7 @@ impl VecCapacity for Vec { /// let mut vec = b"Hello, ".to_vec(); /// const WORLD: &[u8] = b"World!"; /// - /// let mut extra: OutSlice = vec.reserve_uninit(WORLD.len()); + /// let mut extra: Out<'_, [u8]> = vec.reserve_uninit(WORLD.len()); /// extra.r().copy_from_slice(WORLD); /// /// // `.reserve_uninit()` guarantees the following properties: @@ -154,23 +154,22 @@ impl VecCapacity for Vec { /// ``` #[inline] fn reserve_uninit (self: &'_ mut Vec, additional: usize) - -> OutSlice<'_, T> + -> Out<'_, [T]> { self.reserve(additional); let (_, extra) = self.split_at_extra_cap(); unsafe { // Safety: `Vec` guarantees that `cap >= len + additional` and // thus that `cap - len >= additional`. - extra.get_out(.. additional) - .unwrap_or_else(|| hint::unreachable_unchecked()) + extra.get_out_unchecked(.. additional) } } - /// Gets an [`&out [T]` slice][`OutSlice`] (of `self.capacity()` elements) + /// Gets an [`&out [T]`][`Out`] slice (of `self.capacity()` elements) /// to the backing buffer. #[inline] fn get_backing_buffer (self: &'_ mut Vec) - -> OutSlice<'_, T> + -> Out<'_, [T]> where T : Copy, // Opinionated stance against accidental memory leaks { @@ -216,7 +215,7 @@ impl VecCapacity for Vec { /// ``` #[inline] fn get_backing_buffer_manually_drop (self: &'_ mut Vec) - -> OutSlice<'_, T> + -> Out<'_, [T]> { let capacity = self.capacity(); unsafe { @@ -228,7 +227,7 @@ impl VecCapacity for Vec { slice::from_raw_parts_mut( self.as_mut_ptr().cast::>(), capacity, - ).as_out::<[T]>() + ).as_out() } } @@ -315,7 +314,7 @@ impl VecExtendFromReader for Vec { mut reader: R, ) -> io::Result<()> { - let buf: OutSlice<'_, u8> = self.reserve_uninit(count); + let buf: Out<'_, [u8]> = self.reserve_uninit(count); let buf: &mut [u8] = reader.read_into_uninit_exact(buf)?; let count: usize = buf.len(); debug_assert_eq!( diff --git a/src/lib.rs b/src/lib.rs index 3aba909..569e97d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,9 +26,8 @@ pub mod prelude { ManuallyDropMut, VecCapacity, }, - out_references::{ + out_ref::{ Out, - OutSlice, }, uninit_array, }; @@ -44,7 +43,7 @@ pub mod read; pub -mod out_references; +mod out_ref; #[doc(hidden)] pub use ::core; @@ -59,7 +58,7 @@ pub use ::core; /// /// let mut reader = &b"Hello, World!"[..]; /// let mut backing_array = uninit_array![u8; 4]; // : [MaybeUninit; 4] -/// let buf = OutSlice::from(&mut backing_array[..]); +/// let buf = backing_array.as_out(); /// assert_eq!( /// reader.read_into_uninit_exact(buf).unwrap(), /// b"Hell", diff --git a/src/out_references.rs b/src/out_ref.rs similarity index 55% rename from src/out_references.rs rename to src/out_ref.rs index e497132..a364e54 100644 --- a/src/out_references.rs +++ b/src/out_ref.rs @@ -1,17 +1,21 @@ +use crate::{ + extension_traits::MaybeUninitExt, +}; use ::core::{ mem::{self, ManuallyDrop, MaybeUninit, }, + ptr, slice, }; /// Wrapper expressing the semantics of `&out T` references -/// (slim pointer (`T : Sized`)). /// -/// In other words, this has the semantics of `&'out mut MaybeUninit`, but +/// In other words, this has the semantics of `&'out mut MaybeUninit` but /// for the ability to write garbage (`MaybeUninit::uninit()`) into it -/// (else coercing `&mut T` to `&out T = Out` would be unsound). +/// (else [coercing `&mut T` to `&out T = Out` would be +/// unsound][`Out::as_uninit_mut`]). /// /// This means that the reference may point to uninitialized memory (or not), /// and thus that writes to the pointee will not call the `.drop()` destructor. @@ -30,10 +34,23 @@ use ::core::{ /// before the [`.as_out()`][`crate::AsOut`] "coercion". #[repr(transparent)] pub -struct Out<'out, T : 'out> ( - &'out mut MaybeUninit, +struct Out<'out, T : 'out + ?Sized> ( + ptr::NonNull, + ::core::marker::PhantomData<&'out mut T>, ); +impl<'out, T : 'out> From<&'out mut MaybeUninit> for Out<'out, T> { + #[inline] + fn from (p: &'out mut MaybeUninit) + -> Out<'out, T> + { + Out( + ptr::NonNull::>::from(p).cast(), + Default::default(), + ) + } +} + impl<'out, T : 'out> From<&'out mut T> for Out<'out, T> where T : Copy, // prevent accidentally leaking memory @@ -42,13 +59,16 @@ where fn from (p: &'out mut T) -> Out<'out, T> { - Out(unsafe { + unsafe { // # Safety // + // - `T` and `MaybeUninit` have the same layout + // (`#[repr(transparent)]`) + // // - The API does not allow to write `MaybeUninit::uninit()` into // the pointee. - mem::transmute(p) - }) + mem::transmute::<_, &'out mut MaybeUninit>(p).into() + } } } @@ -60,42 +80,59 @@ impl<'out, T : 'out> From<&'out mut ManuallyDrop> for Out<'out, T> { fn from (p: &'out mut ManuallyDrop) -> Out<'out, T> { - Out(unsafe { + unsafe { // # Safety // + // - `ManuallyDrop` and `MaybeUninit` have the same layout + // (`#[repr(transparent)]`) + // // - The API does not allow to write `MaybeUninit::uninit()` into // the pointee. - mem::transmute(p) - }) + mem::transmute::<_, &'out mut MaybeUninit>(p).into() + } } } -impl<'out, T : 'out> From<&'out mut MaybeUninit> for Out<'out, T> { +impl<'out, T : 'out + ?Sized> Out<'out, T> { #[inline] - fn from (p: &'out mut MaybeUninit) - -> Out<'out, T> + pub + fn new (p: impl Into>) + -> Self { - Out(p) + p.into() } -} -impl<'out, T : 'out> Out<'out, T> { #[inline] pub - fn new (p: impl Into>) - -> Self + fn reborrow<'reborrow> (self: &'reborrow mut Out<'out, T>) + -> Out<'reborrow, T> + where + 'out : 'reborrow, { - p.into() + Out(self.0, Default::default()) } + /// Shorthand for [`.reborrow()`][`Out::reborrow`] + #[inline] + pub + fn r<'reborrow> (self: &'reborrow mut Out<'out, T>) + -> Out<'reborrow, T> + where + 'out : 'reborrow, + { + self.reborrow() + } +} + +impl<'out, T : 'out> Out<'out, T> { #[inline] pub fn write (self: Out<'out, T>, value: T) -> &'out mut T { - *self.0 = MaybeUninit::new(value); unsafe { - // Safety: pointee has just been write-initialized. + // Safety: this writes a valid (non garbage) value to the pointee + self.0.as_ptr().write(value); self.assume_init() } } @@ -105,13 +142,24 @@ impl<'out, T : 'out> Out<'out, T> { fn replace (self: Out<'out, T>, value: T) -> (MaybeUninit, &'out mut T) { - ( - mem::replace(self.0, MaybeUninit::new(value)), - unsafe { - // Safety: pointee has just been write-initialized. - self.assume_init() - }, - ) + unsafe { + // # Safety + // + // - This: + // + // 1. writes a valid value to the pointee, + // + // 2. extracts the previous value as now owned `MaybeUninit` + // + // Thus it does not not `assume_init()` the pointee nor write + // garbage to it. + // + // - Thanks to write the pointee is known to be initialized. + ( + ptr::replace(self.0.as_ptr().cast(), MaybeUninit::new(value)), + self.assume_init(), + ) + } } #[inline] @@ -119,158 +167,139 @@ impl<'out, T : 'out> Out<'out, T> { fn as_mut_ptr (self: &'_ mut Out<'out, T>) -> *mut T { - self.0.as_mut_ptr() + self.0.as_ptr() } #[inline] pub - fn reborrow<'reborrow> (self: &'reborrow mut Out<'out, T>) - -> Out<'reborrow, T> - where - 'out : 'reborrow, + unsafe + fn assume_init (mut self: Out<'out, T>) + -> &'out mut T { - Out(&mut *self.0) + &mut *self.as_mut_ptr() } - /// Shorthand for [`.reborrow()`][`Out::reborrow`] + /// # Safety + /// + /// The obtained reference cannot be used to write garbage + /// (`MaybeUninit::uninit()`) into the pointee. + /// + /// # Counter-example + /// + /// The following code is Undefined Behavior + /// + /// ```rust,no_run + /// use ::uninit::prelude::*; + /// + /// let mut my_box = Box::new(42); + /// let at_my_box: Out<'_, Box> = + /// my_box + /// .manually_drop_mut() + /// .as_out() + /// ; + /// // Overwrite `my_box` with uninitialized bytes / garbage content. + /// unsafe { + /// *at_my_box.as_uninit_mut() = MaybeUninit::uninit(); + /// } + /// // Runs the destructor for a `Box` using a garbage pointer that + /// // may thus point anywhere in memory! + /// drop(my_box) + /// ``` #[inline] pub - fn r<'reborrow> (self: &'reborrow mut Out<'out, T>) - -> Out<'reborrow, T> - where - 'out : 'reborrow, + unsafe + fn as_uninit_mut (self: Out<'out, T>) + -> &'out mut MaybeUninit { - self.reborrow() + &mut *({self}.as_mut_ptr().cast()) } +} +impl<'out, T : 'out> Default for Out<'out, [T]> { #[inline] - pub - unsafe - fn assume_init (mut self: Out<'out, T>) -> &'out mut T + fn default () + -> Self { - &mut *self.as_mut_ptr() + <&mut [MaybeUninit]>::into(&mut []) } } -/// Wrapper expressing the semantics of `&out [T]` references -/// (fat pointer to a slice). -/// -/// In other words, this has the semantics of `&'out mut [MaybeUninit]`, but -/// for the ability to write garbage (`MaybeUninit::uninit()`) into it -/// (else coercing `&mut [T]` to `&out [T] = OutSlice` would be unsound). -/// -/// This means that the reference may point to uninitialized memory (or not), -/// and thus that writes to the pointee will not call the `.drop()` destructor. -/// -/// This type can be [trivially constructed][`crate::AsOut`] from: -/// -/// - a `&'out mut [MaybeUninit]` (main point of the type), -/// -/// - a `&'out mut [T]` (to keep the ergonomics of being able to overwrite an -/// already initialized value). -/// -/// - To avoid "accidentally" leaking memory in this second case, -/// either `T` must be `Copy` (sufficient condition to prove there is -/// no drop glue), or you must first call -/// [`.manually_drop_mut()`][`crate::ManuallyDropMut`] -/// before the [`.as_out()`][`crate::AsOut`] "coercion". -#[repr(transparent)] -pub -struct OutSlice<'out, T : 'out> ( - &'out mut [MaybeUninit], -); - -impl<'out, T : 'out> From<&'out mut [T]> for OutSlice<'out, T> +impl<'out, T : 'out> From<&'out mut [T]> for Out<'out, [T]> where T : Copy, { #[inline] fn from (slice: &'out mut [T]) - -> OutSlice<'out, T> + -> Out<'out, [T]> { - let len = slice.len(); - OutSlice(unsafe { - // # Safety - // - // - The API does not allow to write `MaybeUninit::uninit()` into - // the pointee. - slice::from_raw_parts_mut( - slice.as_mut_ptr().cast(), - len, - ) - }) + Out( + slice.into(), + Default::default(), + ) } } -impl<'out, T : 'out> From<&'out mut [ManuallyDrop]> for OutSlice<'out, T> { +impl<'out, T : 'out> From<&'out mut [ManuallyDrop]> for Out<'out, [T]> { #[inline] fn from (slice: &'out mut [ManuallyDrop]) - -> OutSlice<'out, T> + -> Out<'out, [T]> { - let len = slice.len(); - OutSlice(unsafe { + unsafe { // # Safety // // - The API does not allow to write `MaybeUninit::uninit()` into // the pointee. - slice::from_raw_parts_mut( - slice.as_mut_ptr().cast(), - len, + Out( + ptr::NonNull::new_unchecked( + slice as *mut [ManuallyDrop] as *mut [T] + ), + Default::default(), ) - }) + } } } -impl<'out, T : 'out> From<&'out mut [MaybeUninit]> for OutSlice<'out, T> { +impl<'out, T : 'out> From<&'out mut [MaybeUninit]> for Out<'out, [T]> { #[inline] fn from (slice: &'out mut [MaybeUninit]) - -> OutSlice<'out, T> + -> Out<'out, [T]> { - OutSlice(slice) + unsafe { + Out( + ptr::NonNull::new_unchecked( + slice as *mut [MaybeUninit] as *mut [T] + ), + Default::default(), + ) + } } } -impl<'out, T : 'out> OutSlice<'out, T> { - #[inline] - pub - fn new (slice: impl Into>) - -> Self - { - slice.into() - } - +impl<'out, T : 'out> Out<'out, [T]> { #[inline] pub fn as_mut_ptr (self: &'_ mut Self) -> *mut T { - self.0.as_mut_ptr().cast() + self.0.as_ptr() as *mut T } + /// # Safety + /// + /// The obtained reference cannot be used to write garbage + /// (`MaybeUninit::uninit()`) into the pointee. #[inline] pub - fn reborrow<'reborrow> (self: &'reborrow mut OutSlice<'out, T>) - -> OutSlice<'reborrow, T> - where - 'out : 'reborrow, - { - OutSlice(&mut *self.0) - } - - /// Shorthand for [`.reborrow()`][`OutSlice::reborrow`] - #[inline] - pub - fn r<'reborrow> (self: &'reborrow mut OutSlice<'out, T>) - -> OutSlice<'reborrow, T> - where - 'out : 'reborrow, + unsafe + fn as_uninit_mut (self: Out<'out, [T]>) + -> &'out mut [MaybeUninit] { - self.reborrow() + &mut *(self.0.as_ptr() as *mut [MaybeUninit]) } #[inline] pub - fn get_out (self: OutSlice<'out, T>, idx: Index) + fn get_out (self: Out<'out, [T]>, idx: Index) -> Option where Index : SliceIndex<'out, T>, @@ -279,10 +308,12 @@ impl<'out, T : 'out> OutSlice<'out, T> { type Output = Out<'out, T>; #[inline] - fn idx (self: usize, slice: OutSlice<'out, T>) + fn idx (self: usize, slice: Out<'out, [T]>) -> Option> { - slice.0.get_mut(self).map(Out::new) + unsafe { slice.as_uninit_mut() } + .get_mut(self) + .map(Out::new) } } @@ -291,13 +322,15 @@ impl<'out, T : 'out> OutSlice<'out, T> { ) => ( $( impl<'out, T : 'out> SliceIndex<'out, T> for $Range { - type Output = OutSlice<'out, T>; + type Output = Out<'out, [T]>; #[inline] - fn idx (self: Self, slice: OutSlice<'out, T>) - -> Option> + fn idx (self: Self, slice: Out<'out, [T]>) + -> Option> { - slice.0.get_mut(self).map(OutSlice::new) + unsafe { slice.as_uninit_mut() } + .get_mut(self) + .map(Out::new) } } )* @@ -321,10 +354,35 @@ impl<'out, T : 'out> OutSlice<'out, T> { idx.idx(self) } + /// # Safety + /// + /// The given `idx` mut be in bounds. + #[inline] + pub + unsafe + fn get_out_unchecked (self: Out<'out, [T]>, idx: Index) + -> Index::Output + where + Index : SliceIndex<'out, T>, + { + self.get_out(idx) + .unwrap_or_else(|| if cfg!(debug_assertions) { + panic!(concat!( + "Attempted to index out of bounds through unchecked ", + "indexing (this was detected thanks to a check still ", + "being present in debug mode).\n", + r"/!\ THIS IS A BUG AND A SOUNDNESS ISSUE /!\", "\n", + "Please submit an issue ASAP.", + )); + } else { + ::core::hint::unreachable_unchecked() + }) + } + #[inline] pub unsafe - fn assume_init (mut self: OutSlice<'out, T>) -> &'out mut [T] + fn assume_init (mut self: Out<'out, [T]>) -> &'out mut [T] { let len = self.len(); slice::from_raw_parts_mut( @@ -338,7 +396,7 @@ impl<'out, T : 'out> OutSlice<'out, T> { /// /// It returns a read-writable slice to the initialized bytes for /// convenience (automatically - /// [`assume_init`][`OutSlice::assume_init`]-ed). + /// [`assume_init`][`Out::assume_init`]-ed). /// /// # Panic /// @@ -361,7 +419,7 @@ impl<'out, T : 'out> OutSlice<'out, T> { /// /// let mut array = uninit_array![_; 13]; /// assert_eq!( - /// array.as_out::<[u8]>().copy_from_slice(b"Hello, World!"), + /// array.as_out().copy_from_slice(b"Hello, World!"), /// b"Hello, World!", /// ); /// // we can thus soundly `assume_init` our array: @@ -378,7 +436,7 @@ impl<'out, T : 'out> OutSlice<'out, T> { /// ``` pub fn copy_from_slice ( - self: OutSlice<'out, T>, + mut self: Out<'out, [T]>, source_slice: &'_ [T], ) -> &'out mut [T] where @@ -392,9 +450,12 @@ impl<'out, T : 'out> OutSlice<'out, T> { // // - the `copy_nonoverlapping()` call guarantees that the buffer // has been initialized. - self.0.copy_from_slice( - crate::extension_traits::MaybeUninitExt::from_ref(source_slice) - ); + self.r() + .as_uninit_mut() + .copy_from_slice( + <[MaybeUninit]>::from_ref(source_slice) + ) + ; self.assume_init() } } @@ -407,7 +468,7 @@ impl<'out, T : 'out> OutSlice<'out, T> { #[inline] pub fn fill_with ( - mut self: OutSlice<'out, T>, + mut self: Out<'out, [T]>, mut factory: impl FnMut(usize) -> T, ) -> &'out mut [T] { @@ -424,32 +485,34 @@ impl<'out, T : 'out> OutSlice<'out, T> { /// `.reborrow().into_iter()` #[inline] pub - fn iter_out (self: &'_ mut OutSlice<'out, T>) - -> iter::IterOut<'_, T> + fn iter_out<'reborrow> (self: &'reborrow mut Out<'out, [T]>) + -> iter::IterOut<'reborrow, T> { self.r().into_iter() } #[inline] pub - fn split_at_out (self: OutSlice<'out, T>, idx: usize) - -> (OutSlice<'out, T>, OutSlice<'out, T> ) + fn split_at_out (self: Out<'out, [T]>, idx: usize) + -> (Out<'out, [T]>, Out<'out, [T]> ) { - let (left, right) = self.0.split_at_mut(idx); + let (left, right) = unsafe { self.as_uninit_mut() }.split_at_mut(idx); (left.into(), right.into()) } } /// `Deref` into `[MaybeUninit]` to get access to the slice length related /// getters. -impl<'out, T : 'out> ::core::ops::Deref for OutSlice<'out, T> { +impl<'out, T : 'out> ::core::ops::Deref for Out<'out, [T]> { type Target = [MaybeUninit]; #[inline] fn deref (self: &'_ Self) - -> &'_ Self::Target + -> &'_ [MaybeUninit] { - &*self.0 + unsafe { + &*(self.0.as_ptr() as *const [MaybeUninit]) + } } } @@ -461,7 +524,7 @@ mod private { trait SliceIndex<'out, T> { type Output : 'out; - fn idx (self: Self, slice: OutSlice<'out, T>) + fn idx (self: Self, slice: Out<'out, [T]>) -> Option ; } @@ -472,19 +535,22 @@ mod iter { use super::*; pub - struct IterOut<'out, T : 'out> /* = */ ( + struct IterOut<'out, T : 'out> { + /// Points to the remaining elements of the iterator. pub - OutSlice<'out, T>, - ); + slice: Out<'out, [T]>, + + _non_exhaustive: (), + } - impl<'out, T : 'out> IntoIterator for OutSlice<'out, T> { + impl<'out, T : 'out> IntoIterator for Out<'out, [T]> { type Item = Out<'out, T>; type IntoIter = IterOut<'out, T>; - fn into_iter (self: OutSlice<'out, T>) + fn into_iter (self: Out<'out, [T]>) -> IterOut<'out, T> { - IterOut(self) + IterOut { slice: self, _non_exhaustive: () } } } @@ -495,14 +561,13 @@ mod iter { fn next (self: &'_ mut IterOut<'out, T>) -> Option> { - let this = &mut self.0; - if this.is_empty() { return None; } - let slice = mem::replace(this, <&mut [MaybeUninit]>::into(&mut [])); + if self.slice.is_empty() { return None; } + let slice = mem::replace(&mut self.slice, Out::default()); let (first, rest) = slice.split_at_out(1); - *this = rest; - Some(first.get_out(0).unwrap_or_else(|| unsafe { - ::core::hint::unreachable_unchecked() - })) + self.slice = rest; + Some(unsafe { + first.get_out_unchecked(0) + }) } } } diff --git a/src/read/impls.rs b/src/read/impls.rs index 16be8fb..345d9b9 100644 --- a/src/read/impls.rs +++ b/src/read/impls.rs @@ -10,7 +10,7 @@ impl ReadIntoUninit for &'_ mut R { #[inline] fn read_into_uninit<'buf> ( self: &'_ mut Self, - buf: OutSlice<'buf, u8>, + buf: Out<'buf, [u8]>, ) -> io::Result<&'buf mut [u8]> { (**self).read_into_uninit(buf) @@ -26,7 +26,7 @@ unsafe impl ReadIntoUninit for &'_ [u8] { fn read_into_uninit<'buf> ( self: &'_ mut Self, - buf: OutSlice<'buf, u8>, + buf: Out<'buf, [u8]>, ) -> io::Result<&'buf mut [u8]> { let count = ::std::cmp::min(buf.len(), self.len()); @@ -70,7 +70,7 @@ macro_rules! impl_ReadIntoUninit_for_impl_BufRead {( #[inline] fn read_into_uninit<'buf> ( self: &'_ mut Self, - buf: OutSlice<'buf, u8>, + buf: Out<'buf, [u8]>, ) -> io::Result<&'buf mut [u8]> { let buf = { @@ -113,7 +113,7 @@ impl ReadIntoUninit for Box { #[inline] fn read_into_uninit<'buf> ( self: &'_ mut Self, - buf: OutSlice<'buf, u8>, + buf: Out<'buf, [u8]>, ) -> io::Result<&'buf mut [u8]> { (**self).read_into_uninit(buf) diff --git a/src/read/mod.rs b/src/read/mod.rs index d27c1f4..51cb5e3 100644 --- a/src/read/mod.rs +++ b/src/read/mod.rs @@ -37,7 +37,7 @@ trait ReadIntoUninit : Read { /// [`.read()`][`Read::read`] for more information. fn read_into_uninit<'buf> ( self: &'_ mut Self, - buf: OutSlice<'buf, u8>, + buf: Out<'buf, [u8]>, ) -> io::Result<&'buf mut [u8]> ; @@ -50,7 +50,7 @@ trait ReadIntoUninit : Read { /// information. fn read_into_uninit_exact<'buf> ( self: &'_ mut Self, - mut buf: OutSlice<'buf, u8>, + mut buf: Out<'buf, [u8]>, ) -> io::Result<&'buf mut [u8]> { { @@ -118,7 +118,7 @@ macro_rules! auto_impl {( { ::read_into_uninit( self, - buf.as_out_slice(), + buf.as_out(), ).map(|x| x.len()) } } @@ -131,7 +131,7 @@ unsafe impl ReadIntoUninit for R { default fn read_into_uninit<'buf> ( self: &'_ mut Self, - buf: OutSlice<'buf, u8>, + buf: Out<'buf, [u8]>, ) -> io::Result<&'buf mut [u8]> { let buf = buf.fill_with(|_| 0); @@ -142,7 +142,7 @@ unsafe impl ReadIntoUninit for R { default fn read_into_uninit_exact<'buf> ( self: &'_ mut Self, - buf: OutSlice<'buf, u8>, + buf: Out<'buf, [u8]>, ) -> io::Result<&'buf mut [u8]> { let buf = buf.fill_with(|_| 0); @@ -205,12 +205,12 @@ mod chain { { fn read_into_uninit<'buf> ( self: &'_ mut Self, - mut buf: OutSlice<'buf, u8>, + mut buf: Out<'buf, [u8]>, ) -> io::Result<&'buf mut [u8]> { let len = buf.len(); if len == 0 { - return Ok(self.copy_from_slice(&[])); + return Ok(buf.copy_from_slice(&[])); } if self.first_done.not() { let buf_ = self.first.read_into_uninit(buf.r())?; From 91d4ee7849e4b5574fbb8cea47a16a37091600fc Mon Sep 17 00:00:00 2001 From: Daniel Henry-Mantilla Date: Sat, 29 Feb 2020 21:01:28 +0100 Subject: [PATCH 5/5] Improve code as per @HeroicKatora suggestions. Co-Authored-By: Andreas Molzer --- Cargo.toml | 5 +- README.md | 12 +- src/extension_traits/as_out.rs | 1 + src/extension_traits/boxed.rs | 86 +++++- src/extension_traits/manually_drop_mut.rs | 33 +- src/extension_traits/maybe_uninit.rs | 41 ++- src/extension_traits/mod.rs | 25 +- src/extension_traits/vec.rs | 155 +++++++--- src/lib.rs | 25 +- src/out_ref.rs | 349 ++++++++++++++++++---- src/read/impls.rs | 1 + src/read/mod.rs | 14 +- src/utils/macros.rs | 14 + src/utils/prelude.rs | 17 +- 14 files changed, 632 insertions(+), 146 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 12e18b8..e829169 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [lib] -crate-type = ["rlib", "cdylib"] +crate-type = ["rlib"] [package] name = "uninit" @@ -21,12 +21,13 @@ license = "MIT" require_unsafe_in_body = "0.2.0" [features] +std = [] nightly = [] specialization = ["nightly"] const_generics = ["nightly"] chain = [] -default = [] +default = ["std"] [package.metadata.docs.rs] features = [ "nightly" ] diff --git a/README.md b/README.md index 815e0c5..c9ee69b 100644 --- a/README.md +++ b/README.md @@ -208,9 +208,9 @@ So, the objective of this crate is double: [`Read`]: https://doc.rust-lang.org/1.36.0/std/io/trait.Read.html [`mem::uninitialized`]: https://doc.rust-lang.org/core/mem/fn.uninitialized.html [`MaybeUninit`]: https://doc.rust-lang.org/core/mem/union.MaybeUninit.html -[`.assume_init_by_ref()`]: `crate::extension_traits::MaybeUninitExt::assume_init_by_ref` -[`.assume_init_by_mut()`]: `crate::extension_traits::MaybeUninitExt::assume_init_by_mut` -[`uninit_array!`]: `uninit_array` -[`Vec::reserve_uninit`]: `crate::extension_traits::VecCapacity::reserve_uninit` -[Initialize an uninitialized buffer with `.copy_from_slice()`]: `crate::out_ref::Out::copy_from_slice` -[`ReadIntoUninit`]: `crate::read::ReadIntoUninit` +[`.assume_init_by_ref()`]: https://docs.rs/uninit/0.2.0/uninit/extension_traits/trait.MaybeUninitExt.html#tymethod.assume_init_by_ref +[`.assume_init_by_mut()`]: https://docs.rs/uninit/0.2.0/uninit/extension_traits/trait.MaybeUninitExt.html#tymethod.assume_init_by_mut +[`uninit_array!`]: https://docs.rs/uninit/0.2.0/uninit/macro.uninit_byte_array.html +[`Vec::reserve_uninit`]: https://docs.rs/uninit/0.2.0/uninit/extension_traits/trait.VecCapacity.html#tymethod.reserve_uninit +[Initialize an uninitialized buffer with `.copy_from_slice()`]: https://docs.rs/uninit/0.2.0/uninit/out_ref/struct.Out.html#method.copy_from_slice +[`ReadIntoUninit`]: https://docs.rs/uninit/0.2.0/uninit/read/trait.ReadIntoUninit.html diff --git a/src/extension_traits/as_out.rs b/src/extension_traits/as_out.rs index 639e095..05b9206 100644 --- a/src/extension_traits/as_out.rs +++ b/src/extension_traits/as_out.rs @@ -41,6 +41,7 @@ use crate::extension_traits::ManuallyDropMut; /// ``` pub trait AsOut { + #[allow(missing_docs)] fn as_out<'out> (self: &'out mut Self) -> Out<'out, Pointee> ; diff --git a/src/extension_traits/boxed.rs b/src/extension_traits/boxed.rs index bede5bf..eeaa3b3 100644 --- a/src/extension_traits/boxed.rs +++ b/src/extension_traits/boxed.rs @@ -1,8 +1,10 @@ -use_prelude!(); +use_prelude!(); cfg_std! { -use self::private::Sealed; -mod private { pub trait Sealed : Sized {} } -impl Sealed for Box> {} +mod private { + pub trait Sealed : Sized {} + impl Sealed for Box<::core::mem::MaybeUninit> + {} +} /// Extension trait for uninitalized `Box` allocations and /// the optimized delayed-initialization pattern. @@ -147,8 +149,9 @@ impl BoxUninit for Box> { } /// Extension trait for uninitalized `Box` allocations and /// the optimized delayed-initialization pattern. +#[allow(missing_docs)] pub -trait BoxUninit : Sealed { +trait BoxUninit : private::Sealed { type T; fn uninit () -> Self @@ -160,3 +163,76 @@ trait BoxUninit : Sealed { -> Option ; } + +mod private2 { + pub trait Sealed {} + impl Sealed for Box<[::core::mem::MaybeUninit]> + {} + impl Sealed for Box<::core::mem::MaybeUninit> + {} +} + +impl BoxAssumeInit for Box<[MaybeUninit]> { + #[allow(missing_docs)] + type Ret = [T]; + + + /// Allows to "`.assume_init()`" a boxed `[MaybeUninit]`. + /// + /// # Safety + /// + /// - This has the same safety requirements as + /// [`.assume_init()`][`MaybeUninit::assume_init`]. + #[inline] + unsafe + fn assume_init (this: Box<[MaybeUninit]>) + -> Box<[T]> + { + let len = this.len(); + let ptr = Box::leak(this).as_mut_ptr(); + Box::from_raw(slice::from_raw_parts_mut( + ptr.cast(), len, + )) + } +} + +impl BoxAssumeInit for Box> { + type Ret = T; + + /// Allows to "`.assume_init()`" a boxed `MaybeUninit`. + /// + /// # Safety + /// + /// - This has the same safety requirements as + /// [`.assume_init()`][`MaybeUninit::assume_init`]. + #[inline] + unsafe + fn assume_init (this: Box>) + -> Box + { + Box::from_raw(Box::into_raw(this).cast()) + } +} + +/// Extension trait to `.assume_init()` through a `Box`. +/// +/// This is a compatibility helper trait. For versions of Rust where the +/// `feature(box_uninit)` is unstable, this trait enables the feature in stable +/// Rust. This may trigger an `unstable_name_collisions` lint, but this is fine, +/// since the implementation is the same. You can dismiss that lint with: +/// +/// ```rust +/// #![allow(unstable_name_collisions)] +/// ``` +#[allow(missing_docs)] +pub +trait BoxAssumeInit : private2::Sealed { + type Ret : ?Sized; + + unsafe + fn assume_init (this: Self) + -> Box + ; +} + +} // cfg_std! diff --git a/src/extension_traits/manually_drop_mut.rs b/src/extension_traits/manually_drop_mut.rs index 523d4e0..116ebeb 100644 --- a/src/extension_traits/manually_drop_mut.rs +++ b/src/extension_traits/manually_drop_mut.rs @@ -13,23 +13,50 @@ use ::core::mem::ManuallyDrop; /// /// # Example /// +/// The following fails to compile because of the missing `Copy` bound: +/// /// ```rust,compile_fail +/// use ::uninit::prelude::*; /// use ::core::cell::Cell; -/// use ::uninit::prelude::{AsOut, ManuallyDropMut}; /// /// let mut cell = Cell::new(0); /// cell.as_out().write(Cell::new(42)); // Error, not `Copy` /// assert_eq!(cell.get(), 42); /// ``` /// +/// We see here that the `Copy` bound can be too restrictive. By calling +/// `.manually_drop_mut()`, we no longer need to satisfy this `Copy` bound; but +/// then we need to be careful with memory leaks. +/// +/// Since `::core::mem::needs_drop::>() == false`, there is nothing to +/// worry about: +/// /// ```rust +/// use ::uninit::prelude::*; /// use ::core::cell::Cell; -/// use ::uninit::prelude::{AsOut, ManuallyDropMut}; /// /// let mut cell = Cell::new(0); /// cell.manually_drop_mut().as_out().write(Cell::new(42)); // OK /// assert_eq!(cell.get(), 42); /// ``` +/// +/// # Counterexample +/// +/// ```rust +/// use ::uninit::prelude::*; +/// use ::std::rc::Rc; +/// +/// let rc = Rc::new(()); +/// assert_eq!(Rc::strong_count(&rc), 1); +/// let mut rc2 = Some(Rc::clone(&rc)); +/// assert_eq!(Rc::strong_count(&rc), 2); +/// // This overwrites `rc2` without running any destructor whatsoever, hence +/// // leaking the `rc` clone. +/// rc2.manually_drop_mut().as_out().write(None); +/// assert_eq!(Rc::strong_count(&rc), 2); +/// assert!(Rc::try_unwrap(rc).is_err()); +/// ``` +#[allow(missing_docs)] pub trait ManuallyDropMut { type Ret : ?Sized; @@ -42,6 +69,7 @@ trait ManuallyDropMut { impl ManuallyDropMut for [T] { type Ret = [ManuallyDrop]; + #[inline] fn manually_drop_mut<'__> (self: &'__ mut [T]) -> &'__ mut [ManuallyDrop] { @@ -58,6 +86,7 @@ impl ManuallyDropMut for [T] { impl ManuallyDropMut for T { type Ret = ManuallyDrop; + #[inline] fn manually_drop_mut<'__> (self: &'__ mut T) -> &'__ mut ManuallyDrop { diff --git a/src/extension_traits/maybe_uninit.rs b/src/extension_traits/maybe_uninit.rs index 1261a37..c2f1349 100644 --- a/src/extension_traits/maybe_uninit.rs +++ b/src/extension_traits/maybe_uninit.rs @@ -39,26 +39,39 @@ use_prelude!(); /// [the `&out` reference abstraction][`crate::out_ref`]. pub trait MaybeUninitExt { + #[allow(missing_docs)] type T : ?Sized; + /// Converts a `&MaybeUninit<_>` to a `& _`. + /// /// # Safety /// - /// - The `Self::T` that `self` points to must be initialized. + /// Don't be lured by the reference: this has the same safety requirements + /// that [`.assume_init`][`MaybeUninit::assume_init`] does. Mainly: + /// + /// - The `Self::T` that `self` points to must be initialized. unsafe fn assume_init_by_ref (self: &'_ Self) - -> &'_ Self::T + -> &'_ Self::T ; + /// Converts a `&mut MaybeUninit<_>` to a `&mut _`. + /// /// # Safety /// - /// - The `Self::T` that `self` points to must be initialized. + /// Don't be lured by the `mut` reference: this has the same safety + /// requirements that [`.assume_init`][`MaybeUninit::assume_init`] does. + /// Mainly: + /// + /// - The `Self::T` that `self` points to must be initialized. unsafe fn assume_init_by_mut (self: &'_ mut Self) - -> &'_ mut Self::T + -> &'_ mut Self::T ; + /// Downgrades a `& _` to a `&MaybeUninit<_>`. Rarely useful. fn from_ref (init_ref: &'_ Self::T) - -> &'_ Self + -> &'_ Self ; } @@ -66,9 +79,10 @@ trait MaybeUninitExt { impl MaybeUninitExt for MaybeUninit { type T = T; + #[inline] unsafe fn assume_init_by_ref (self: &'_ Self) - -> &'_ Self::T + -> &'_ Self::T { unsafe { // # Safety @@ -78,9 +92,10 @@ impl MaybeUninitExt for MaybeUninit { } } + #[inline] unsafe fn assume_init_by_mut (self: &'_ mut Self) - -> &'_ mut Self::T + -> &'_ mut Self::T { unsafe { // # Safety @@ -90,8 +105,9 @@ impl MaybeUninitExt for MaybeUninit { } } + #[inline] fn from_ref (some_ref: &'_ Self::T) - -> &'_ Self + -> &'_ Self { unsafe { // # Safety @@ -106,9 +122,10 @@ impl MaybeUninitExt for MaybeUninit { impl MaybeUninitExt for [MaybeUninit] { type T = [T]; + #[inline] unsafe fn assume_init_by_ref (self: &'_ Self) - -> &'_ Self::T + -> &'_ Self::T { unsafe { // # Safety @@ -122,9 +139,10 @@ impl MaybeUninitExt for [MaybeUninit] { } } + #[inline] unsafe fn assume_init_by_mut (self: &'_ mut Self) - -> &'_ mut Self::T + -> &'_ mut Self::T { unsafe { // # Safety @@ -138,8 +156,9 @@ impl MaybeUninitExt for [MaybeUninit] { } } + #[inline] fn from_ref (slice: &'_ Self::T) - -> &'_ Self + -> &'_ Self { unsafe { // # Safety diff --git a/src/extension_traits/mod.rs b/src/extension_traits/mod.rs index a2c37f7..8cb3e9c 100644 --- a/src/extension_traits/mod.rs +++ b/src/extension_traits/mod.rs @@ -1,13 +1,10 @@ +//! Extension traits to enhance external types with useful methods. + pub use self::as_out::{ AsOut, }; mod as_out; -pub use self::boxed::{ - BoxUninit, -}; -mod boxed; - pub use self::manually_drop_mut::{ ManuallyDropMut, }; @@ -18,8 +15,16 @@ pub use self::maybe_uninit::{ }; mod maybe_uninit; -pub use self::vec::{ - VecExtendFromReader, - VecCapacity, -}; -mod vec; +cfg_std! { + pub use self::boxed::{ + BoxUninit, + BoxAssumeInit, + }; + mod boxed; + + pub use self::vec::{ + VecExtendFromReader, + VecCapacity, + }; + mod vec; +} diff --git a/src/extension_traits/vec.rs b/src/extension_traits/vec.rs index 630da24..79e3678 100644 --- a/src/extension_traits/vec.rs +++ b/src/extension_traits/vec.rs @@ -1,8 +1,10 @@ +cfg_std! { use crate::*; use ::core::slice; /// Extension trait for [`Vec`], allowing a non-`unsafe` API to interact /// with the backing buffer / allocation. +#[allow(missing_docs)] pub trait VecCapacity : Sealed { type Item; @@ -23,10 +25,10 @@ trait VecCapacity : Sealed { where Self::Item : Copy, // Opinionated stance against accidental memory leaks ; - fn get_backing_buffer_manually_drop (self: &'_ mut Self) + fn get_backing_buffer_with_leaking_writes (self: &'_ mut Self) -> Out<'_, [Self::Item]> ; - fn into_backing_buffer_manually_drop (self: Self) + fn into_backing_buffer_forget_elems (self: Self) -> Box<[MaybeUninit]> ; } @@ -35,6 +37,7 @@ mod private { pub trait Sealed : Sized {} } use private::Sealed; impl Sealed for Vec {} impl VecCapacity for Vec { + #[allow(missing_docs)] type Item = T; /// Splits the `Vec`'s @@ -43,10 +46,9 @@ impl VecCapacity for Vec { /// /// Imagine this as doing /// `self.get_backing_buffer().split_at_out(self.len())` - /// while upgrading the first half to `&mut [T]` and the second half to a - /// `&mut [MaybeUninit]`. + /// while upgrading the first half to `&mut [T]`. /// - /// # Guarantees (that `unsafe` code may rely on): + /// # Guarantees (that `unsafe` code may rely on) /// /// Given a vector `v`, and `let (xs, extra) = v.split_at_extra_cap()`, /// then: @@ -64,17 +66,21 @@ impl VecCapacity for Vec { /// **`extra.len() ≥ n`**. /// /// For the `extra.len() == n` equality to hold, one must subslice - /// `extra`: `extra = &mut extra[.. n];`. + /// `extra`: + /// + /// - `extra = extra.get_out(.. n).unwrap();`. + /// /// And given the aformentioned guarantees, one can even: - /// `extra = extra.get_mut_unchecked(.. n);` + /// + /// - `extra = extra.get_unchecked_out(.. n);` /// /// This last idiom is covered by /// [`.reserve_uninit(n)`][`VecCapacity::reserve_uninit`]. /// - /// - `extra.as_ptr().cast() == v.as_ptr().add(v.len())`. + /// - `extra.as_ptr() == v.as_ptr().add(v.len())`. /// - /// - Thus, (only) after initializing the first `k` elements of `extra`, - /// it is sound to `v.set_len(v.len() + k);`. + /// - Thus, only after initializing the first `k` elements of `extra`, + /// is it sound to `v.set_len(v.len() + k);`. /// /// # Example /// @@ -106,7 +112,7 @@ impl VecCapacity for Vec { -> (&'_ mut [T], Out<'_, [T]>) { let len = self.len(); - let backing_buffer = self.get_backing_buffer_manually_drop(); + let backing_buffer = self.get_backing_buffer_with_leaking_writes(); let (mut xs, extra) = backing_buffer.split_at_out(len); ( unsafe { @@ -161,7 +167,7 @@ impl VecCapacity for Vec { unsafe { // Safety: `Vec` guarantees that `cap >= len + additional` and // thus that `cap - len >= additional`. - extra.get_out_unchecked(.. additional) + extra.get_unchecked_out(.. additional) } } @@ -173,7 +179,7 @@ impl VecCapacity for Vec { where T : Copy, // Opinionated stance against accidental memory leaks { - self.get_backing_buffer_manually_drop() + self.get_backing_buffer_with_leaking_writes() } /// Same as [`.get_backing_buffer()`][`VecCapacity::get_backing_buffer`] @@ -190,13 +196,21 @@ impl VecCapacity for Vec { /// /// ```rust /// use ::uninit::prelude::*; - /// - /// let mut v = vec![Some(Box::new(42))]; - /// v .get_backing_buffer_manually_drop() + /// use ::std::rc::Rc; + /// + /// let rc = Rc::new(()); + /// assert_eq!(Rc::strong_count(&rc), 1); + /// let mut v = vec![ Some(Rc::clone(&rc)) ]; + /// assert_eq!(Rc::strong_count(&rc), 2); + /// // This overwrites the `rc` clone without running any destructor + /// // whatsoever, hence leaking it. + /// v .get_backing_buffer_with_leaking_writes() /// .get_out(0) /// .unwrap() - /// .write(None) // the box is not freed despite now being unreachable. + /// .write(None) // the `rc` clone is not freed /// ; + /// assert_eq!(Rc::strong_count(&rc), 2); + /// assert!(Rc::try_unwrap(rc).is_err()); /// ``` /// /// # Example @@ -206,7 +220,7 @@ impl VecCapacity for Vec { /// use ::std::cell::Cell; /// /// let mut v = vec![Cell::new(0)]; - /// v .get_backing_buffer_manually_drop() // No drop glue, so this is fine + /// v .get_backing_buffer_with_leaking_writes() // No drop glue, so this is fine /// .get_out(0) /// .unwrap() /// .write(Cell::new(42)) @@ -214,7 +228,7 @@ impl VecCapacity for Vec { /// assert_eq!(v[0].get(), 42); /// ``` #[inline] - fn get_backing_buffer_manually_drop (self: &'_ mut Vec) + fn get_backing_buffer_with_leaking_writes (self: &'_ mut Vec) -> Out<'_, [T]> { let capacity = self.capacity(); @@ -238,7 +252,7 @@ impl VecCapacity for Vec { where T : Copy, // Opinionated stance against accidental memory leaks { - self.into_backing_buffer_manually_drop() + self.into_backing_buffer_forget_elems() } /// Same as [`.into_backing_buffer()`][ @@ -250,8 +264,45 @@ impl VecCapacity for Vec { /// longer knows which are). /// /// ⚠️ **Misusage of this function can thus lead to memory leaks** ⚠️ + /// + /// # Counter-example + /// + /// ```rust + /// use ::uninit::prelude::*; + /// use ::std::rc::Rc; + /// + /// let rc = Rc::new(()); + /// assert_eq!(Rc::strong_count(&rc), 1); + /// let mut v = vec![ Some(Rc::clone(&rc)) ]; + /// assert_eq!(Rc::strong_count(&rc), 2); + /// // This leaks the `rc` clone (but not the heap-allocated array containing it) + /// let _ = v.into_backing_buffer_forget_elems(); + /// assert_eq!(Rc::strong_count(&rc), 2); + /// assert!(Rc::try_unwrap(rc).is_err()); + /// ``` + /// + /// # Example + /// + /// ```rust + /// use ::uninit::prelude::*; + /// + /// let mut v = vec![String::from("Hello!")]; + /// // Good practice: before calling `.into_backing_buffer_forget_elems()` + /// // one ought to `.clear()` the `Vec`: + /// v.clear(); // drops `"Hello!"` + /// let mut strings_buffer: Box<[MaybeUninit]> = + /// v.into_backing_buffer_forget_elems() + /// ; + /// strings_buffer[0] = MaybeUninit::new(String::from("Greetings!")); + /// let strings_buffer: Box<[String]> = unsafe { + /// Box::assume_init(strings_buffer) + /// }; + /// assert_eq!(&*strings_buffer[0], "Greetings!"); + /// // This does free the contained "Greetings!" `String`. + /// drop(strings_buffer); + /// ``` #[inline] - fn into_backing_buffer_manually_drop (self: Vec) + fn into_backing_buffer_forget_elems (self: Vec) -> Box<[MaybeUninit]> { // We need to go through an intermediate max-capacity `Vec` because @@ -281,7 +332,7 @@ impl VecCapacity for Vec { /// bytes, obtained when reading from `R`. /// /// This guarantees that the allocated memory starts uninitialized (before -/// being initialized when read), for maximum performance. +/// being initialized by the read), for maximum performance. /// /// # Example /// @@ -290,33 +341,48 @@ impl VecCapacity for Vec { /// /// let mut reader = &b"World!"[..]; /// let mut vec = b"Greetings, ".to_vec(); -/// vec.extend_from_reader(6, &mut reader).unwrap(); +/// vec.extend_from_reader_exact(6, &mut reader).unwrap(); /// assert_eq!( /// vec, /// b"Greetings, World!", /// ); /// ``` pub -trait VecExtendFromReader { - fn extend_from_reader ( +trait VecExtendFromReader { + /// Tries to extends the `Vec` with up to `max_count` bytes read from + /// `reader`. + fn extend_from_reader ( self: &'_ mut Self, - count: usize, + max_count: usize, + reader: R, + ) -> io::Result + ; + /// Tries to extends the `Vec` with exactly `exact_count` bytes read from + /// `reader`. + fn extend_from_reader_exact ( + self: &'_ mut Self, + exact_count: usize, reader: R, ) -> io::Result<()> ; } -impl VecExtendFromReader for Vec { +macro_rules! make_extend {( + name = $fname:ident, + count_name = $count_param:ident, + read_into_buf = |$reader:ident, $buf:ident| $read_into_buf:expr, + ret_of_count = |$count:ident| -> $Ret:ty { $ret_of_count:expr }, +) => ( #[inline] - fn extend_from_reader ( + fn $fname ( self: &'_ mut Self, - count: usize, - mut reader: R, - ) -> io::Result<()> + $count_param: usize, + mut $reader: R, + ) -> io::Result<$Ret> { - let buf: Out<'_, [u8]> = self.reserve_uninit(count); - let buf: &mut [u8] = reader.read_into_uninit_exact(buf)?; - let count: usize = buf.len(); + let $buf: Out<'_, [u8]> = self.reserve_uninit($count_param); + let buf: &mut [u8] = $read_into_buf?; + let $count: usize = buf.len(); debug_assert_eq!( buf.as_mut_ptr(), self.as_mut_ptr() @@ -324,7 +390,7 @@ impl VecExtendFromReader for Vec { , "This is a bug and a soundness issue. Please submit an issue ASAP", ); - let new_len = self.len().checked_add(count); + let new_len = self.len().checked_add($count); debug_assert!( new_len .map(|new_len| new_len <= self.capacity()) @@ -341,6 +407,23 @@ impl VecExtendFromReader for Vec { new_len.unwrap_or_else(|| hint::unreachable_unchecked()) ); } - Ok(()) + Ok($ret_of_count) + } +)} + +impl VecExtendFromReader for Vec { + make_extend! { + name = extend_from_reader, + count_name = max_count, + read_into_buf = |reader, buf| reader.read_into_uninit(buf), + ret_of_count = |count| -> usize { count }, + } + make_extend! { + name = extend_from_reader_exact, + count_name = exact_count, + read_into_buf = |reader, buf| reader.read_into_uninit_exact(buf), + ret_of_count = |count| -> () { () }, } } + +} // cfg_std! diff --git a/src/lib.rs b/src/lib.rs index 569e97d..7f881fc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,7 @@ feature(doc_cfg, external_doc), doc(include = "../README.md"), )] +//! #![cfg_attr(feature = "specialization", feature(specialization), )] @@ -10,6 +11,13 @@ )] #![deny( elided_lifetimes_in_paths, + missing_docs, + missing_copy_implementations, + missing_debug_implementations, + unused_must_use, +)] +#![cfg_attr(not(feature = "std"), + no_std, )] #[macro_use] @@ -18,19 +26,26 @@ extern crate require_unsafe_in_body; #[macro_use] mod utils; pub mod prelude { + //! Reexports of pervasive items. #[doc(no_inline)] pub use crate::{ extension_traits::{ AsOut, - BoxUninit, ManuallyDropMut, - VecCapacity, }, out_ref::{ Out, }, uninit_array, }; + cfg_std! { + #[doc(no_inline)] + pub use crate::extension_traits::{ + BoxUninit, + BoxAssumeInit, + VecCapacity, + }; + } pub use ::core::mem::MaybeUninit; } @@ -39,8 +54,10 @@ use_prelude!(); pub mod extension_traits; -pub -mod read; +cfg_std! { + pub + mod read; +} pub mod out_ref; diff --git a/src/out_ref.rs b/src/out_ref.rs index a364e54..af4c1ac 100644 --- a/src/out_ref.rs +++ b/src/out_ref.rs @@ -1,3 +1,5 @@ +//! `&out _` references in stable Rust! + use crate::{ extension_traits::MaybeUninitExt, }; @@ -15,7 +17,7 @@ use ::core::{ /// In other words, this has the semantics of `&'out mut MaybeUninit` but /// for the ability to write garbage (`MaybeUninit::uninit()`) into it /// (else [coercing `&mut T` to `&out T = Out` would be -/// unsound][`Out::as_uninit_mut`]). +/// unsound][`Out::as_mut_uninit`]). /// /// This means that the reference may point to uninitialized memory (or not), /// and thus that writes to the pointee will not call the `.drop()` destructor. @@ -32,6 +34,7 @@ use ::core::{ /// no drop glue), or you must first call /// [`.manually_drop_mut()`][`crate::ManuallyDropMut`] /// before the [`.as_out()`][`crate::AsOut`] "coercion". +#[derive(Debug)] #[repr(transparent)] pub struct Out<'out, T : 'out + ?Sized> ( @@ -39,6 +42,22 @@ struct Out<'out, T : 'out + ?Sized> ( ::core::marker::PhantomData<&'out mut T>, ); +// # Safety +// +// `Send`/`Sync` safety is related to mutation, so in that regard there is no +// difference between `Out<'_, T>` and `&'_ mut T` (in other words: +// `MaybeUninit` plays no role in that regard). +// +// Thus `Out` is `{Send,Sync}` if and only if `&mut` is. +unsafe impl<'out, T : ?Sized + 'out> Send for Out<'out, T> +where + &'out mut T : Send, +{} +unsafe impl<'out, T : ?Sized + 'out> Sync for Out<'out, T> +where + &'out mut T : Sync, +{} + impl<'out, T : 'out> From<&'out mut MaybeUninit> for Out<'out, T> { #[inline] fn from (p: &'out mut MaybeUninit) @@ -72,9 +91,13 @@ where } } +#[cfg(doc)] +use crate::extension_traits::ManuallyDropMut; + /// For non-`Copy` types, explicitely transmuting the `mut` reference into one /// that points to a `ManuallyDrop` is required, so as to express how likely it -/// is that memory be leaked. +/// is that memory be leaked. This can be safely achieved by using the +/// [`ManuallyDropMut`] helper. impl<'out, T : 'out> From<&'out mut ManuallyDrop> for Out<'out, T> { #[inline] fn from (p: &'out mut ManuallyDrop) @@ -94,14 +117,7 @@ impl<'out, T : 'out> From<&'out mut ManuallyDrop> for Out<'out, T> { } impl<'out, T : 'out + ?Sized> Out<'out, T> { - #[inline] - pub - fn new (p: impl Into>) - -> Self - { - p.into() - } - + /// Reborrows the `&out _` reference for a shorter lifetime. #[inline] pub fn reborrow<'reborrow> (self: &'reborrow mut Out<'out, T>) @@ -112,7 +128,7 @@ impl<'out, T : 'out + ?Sized> Out<'out, T> { Out(self.0, Default::default()) } - /// Shorthand for [`.reborrow()`][`Out::reborrow`] + /// Shorthand for [`.reborrow()`][`Out::reborrow`]. #[inline] pub fn r<'reborrow> (self: &'reborrow mut Out<'out, T>) @@ -125,6 +141,14 @@ impl<'out, T : 'out + ?Sized> Out<'out, T> { } impl<'out, T : 'out> Out<'out, T> { + /// Write a `value` into the pointee, returning an `.assume_init()`-ed + /// reference to it. + /// + /// # Guarantees (that `unsafe` code may rely on) + /// + /// After the function returns, the pointee is guaranteed to have been + /// initialized; it is thus sound to use that property to manually + /// `assume_init()` it or any chunk of such items. #[inline] pub fn write (self: Out<'out, T>, value: T) @@ -137,9 +161,21 @@ impl<'out, T : 'out> Out<'out, T> { } } + /// Similar to [`.write()`][`Out::write`], but getting the previous value + /// back. Such previous value may or may not be initialized. + /// + /// # Guarantees (that `unsafe` code may rely on) + /// + /// - After the function returns, the pointee is guaranteed to have been + /// initialized; it is thus sound to use that property to manually + /// `assume_init()` it or any chunk of such items. + /// + /// - there is **no such guarantee** regarding the previous value, which + /// is thus only sound to `assume_init()` if the pointee already was + /// (before the call to `.replace()`). #[inline] pub - fn replace (self: Out<'out, T>, value: T) + fn replace (mut self: Out<'out, T>, value: T) -> (MaybeUninit, &'out mut T) { unsafe { @@ -149,19 +185,35 @@ impl<'out, T : 'out> Out<'out, T> { // // 1. writes a valid value to the pointee, // - // 2. extracts the previous value as now owned `MaybeUninit` + // 2. extracts the previous value as a now owned `MaybeUninit` // // Thus it does not not `assume_init()` the pointee nor write // garbage to it. // // - Thanks to write the pointee is known to be initialized. ( - ptr::replace(self.0.as_ptr().cast(), MaybeUninit::new(value)), + mem::replace(self.r().as_mut_uninit(), MaybeUninit::new(value)), self.assume_init(), ) } } + /// Returns a raw mutable pointer to the pointee. + /// + /// # Guarantees (that `unsafe` code may rely on) + /// + /// - The returned pointer does point to the pointee, meaning that if + /// such returned pointer is used to [`.write()`][`::core::ptr::write`] + /// to the pointee, then it is safe to `assume_init()` it. + /// + /// - The returned pointer is non null, well-aligned, and writeable. + /// + /// It is also technically readable: + /// + /// - you can read a `MaybeUninit` out of it after `.cast()`ing it, + /// + /// - otherwise, except when sound to `assume_init()`, the obtained + /// pointer cannot be used to read the value `: T` of the pointee! #[inline] pub fn as_mut_ptr (self: &'_ mut Out<'out, T>) @@ -170,6 +222,17 @@ impl<'out, T : 'out> Out<'out, T> { self.0.as_ptr() } + /// Upgrades the `&out _` (write-only) reference to a read-writeable + /// `&mut _`. + /// + /// # Safety + /// + /// Don't be lured by the `&mut` reference: Rust validity invariants + /// imply that an `&mut` reference is only sound to produce if it points + /// to an initialized value; it is otherwise instant UB. See + /// [`MaybeUninit::assume_init`] for more info about it. Thus: + /// + /// - The pointee must have been initialized. #[inline] pub unsafe @@ -179,14 +242,23 @@ impl<'out, T : 'out> Out<'out, T> { &mut *self.as_mut_ptr() } + /// _Upgrades_ the `&out _` (write-valid-values-only) reference to a + /// `&mut MaybeUninit<_>` (write-anything) reference. + /// /// # Safety /// - /// The obtained reference cannot be used to write garbage - /// (`MaybeUninit::uninit()`) into the pointee. + /// - The obtained reference cannot be used to write garbage + /// (`MaybeUninit::uninit()`) into the pointee. + /// + /// This means that it can thus **not be fed to opaque APIs!!** + /// + /// - Exception: if the given `&out` reference has originated from a + /// `&mut MaybeUninit<_>`, then calling `.as_mut_uninit()` is a sound + /// way to make the trip back. /// /// # Counter-example /// - /// The following code is Undefined Behavior + /// The following code is Undefined Behavior: /// /// ```rust,no_run /// use ::uninit::prelude::*; @@ -199,22 +271,45 @@ impl<'out, T : 'out> Out<'out, T> { /// ; /// // Overwrite `my_box` with uninitialized bytes / garbage content. /// unsafe { - /// *at_my_box.as_uninit_mut() = MaybeUninit::uninit(); + /// *at_my_box.as_mut_uninit() = MaybeUninit::uninit(); /// } /// // Runs the destructor for a `Box` using a garbage pointer that /// // may thus point anywhere in memory! /// drop(my_box) /// ``` + /// + /// A function from an external library must always be seen as opaque + /// (unless its documentation makes implementation-detail guarantees, such + /// as this very crate does), so one cannot rely on its implementation + /// (unless the lib is open source AND you pin-point to that version of the + /// crate, either through `version = "=x.y.z"` or through `git = ..., + /// rev = ...` in `Cargo.toml`). + /// + /// ```rust,ignore + /// // `fn zeroize (out: &'_ mut MaybeUninit) -> &'_ mut u8;` + /// // The author of the crate says it uses that `out` reference to write + /// // `0` to the pointee. + /// use ::some_lib::zeroize; + /// + /// let mut x = 42; + /// let at_x = x.as_out(); + /// // Unsound! The lib implementation is free to write + /// // `MaybeUninit::uninit()` garbage to the pointee! + /// zeroize(unsafe { at_x.as_mut_uninit() }); + /// ``` #[inline] pub unsafe - fn as_uninit_mut (self: Out<'out, T>) + fn as_mut_uninit (self: Out<'out, T>) -> &'out mut MaybeUninit { &mut *({self}.as_mut_ptr().cast()) } } +/// This can be useful to get a `Out<'long ...>` out of a +/// `&'short mut Out<'long ...>` by [`mem::replace`]-ing with a `Out::default()` +/// (_e.g._, to implement an [`Iterator`]). impl<'out, T : 'out> Default for Out<'out, [T]> { #[inline] fn default () @@ -276,47 +371,84 @@ impl<'out, T : 'out> From<&'out mut [MaybeUninit]> for Out<'out, [T]> { } impl<'out, T : 'out> Out<'out, [T]> { + /// Obtains a read-only non-NULL and well-aligned raw pointer to a + /// potentially uninitialized `T`. + /// + /// Unless maybe with interior mutability through raw pointers, there is + /// no case where using this function is more useful than going through + /// [`<[MaybeUninit<_>]>::assume_init_ref()`][`MaybeUninitExt::assume_init_by_ref`]. + /// + /// Worse, the lack of `unsafe`-ty of the method (ignoring the one needed + /// to use the pointer) and its "boring" name may lead to code + /// read-dereferencing the pointer (which implicitly `assume_init()`s it) + /// without having ensured the soundness of such (implicit) `assume_init()`. + #[inline] + pub + fn as_ptr (self: &'_ Self) + -> *const T + { + self.0.as_ptr().cast() + } + + /// Returns a raw mutable pointer to the pointee. + /// + /// See [`Out::as_mut_ptr`] for more info regarding safety and guarantees. #[inline] pub fn as_mut_ptr (self: &'_ mut Self) -> *mut T { - self.0.as_ptr() as *mut T + self.0.as_ptr().cast() } - /// # Safety + /// _Upgrades_ the `&out _` (write-valid-values-only) reference to a + /// `&mut MaybeUninit<_>` (write-anything) reference. /// - /// The obtained reference cannot be used to write garbage - /// (`MaybeUninit::uninit()`) into the pointee. + /// See [`Out::as_mut_uninit`] for more info regarding safety. #[inline] pub unsafe - fn as_uninit_mut (self: Out<'out, [T]>) + fn as_mut_uninit (self: Out<'out, [T]>) -> &'out mut [MaybeUninit] { &mut *(self.0.as_ptr() as *mut [MaybeUninit]) } + /// Main indexing operation on an `&out [_]`. + /// + /// The type `Index` of `idx` may be: + /// + /// - a `usize`, and then `Index::Output` is a `Out` reference to a + /// single element. + /// + /// - a `Range` (_e.g._, `a .. b`), and then `Index::Output` is + /// a `Out<[T]>` reference to a subslice. + /// + /// # Example + /// + /// ```rust + /// use ::uninit::prelude::*; + /// + /// let src: &[u8] = b"Hello, World!"; + /// // Stack-allocate an uninitialized buffer. + /// let mut buf = uninit_array![u8; 256]; + /// // copy `src` into this stack allocated buffer, effectively initializing it. + /// let buf: &mut [u8] = + /// // buf[.. src.len()].as_out() + /// buf.as_out().get_out(.. src.len()).unwrap() + /// .copy_from_slice(src) + /// ; + /// assert_eq!(buf, b"Hello, World!"); + /// buf[7 ..].copy_from_slice(b"Earth!"); + /// assert_eq!(buf, b"Hello, Earth!"); + /// ``` #[inline] pub fn get_out (self: Out<'out, [T]>, idx: Index) -> Option where - Index : SliceIndex<'out, T>, + Index : UsizeOrRange<'out, T>, // renamed for the documentation { - impl<'out, T : 'out> SliceIndex<'out, T> for usize { - type Output = Out<'out, T>; - - #[inline] - fn idx (self: usize, slice: Out<'out, [T]>) - -> Option> - { - unsafe { slice.as_uninit_mut() } - .get_mut(self) - .map(Out::new) - } - } - macro_rules! impl_SliceIndex {( $($Range:ty),+ $(,)? ) => ( @@ -328,14 +460,35 @@ impl<'out, T : 'out> Out<'out, [T]> { fn idx (self: Self, slice: Out<'out, [T]>) -> Option> { - unsafe { slice.as_uninit_mut() } - .get_mut(self) - .map(Out::new) + unsafe { + // Safety: this goes through an ephemeral + // `&mut [MaybeUninit<_>]` reference to be able to + // use slice indexing, but immediately downgrades + // it back to an `Out` reference, so no danger + // of writing garbage data. + slice.as_mut_uninit() + .get_mut(self) + .map(Out::from) + } } } )* )} + impl<'out, T : 'out> SliceIndex<'out, T> for usize { + type Output = Out<'out, T>; + #[inline] + fn idx (self: usize, slice: Out<'out, [T]>) + -> Option> + { + unsafe { + // Safety: ditto + slice.as_mut_uninit() + .get_mut(self) + .map(Out::from) + } + } + } impl_SliceIndex! { // a .. b ::core::ops::Range, @@ -354,16 +507,29 @@ impl<'out, T : 'out> Out<'out, [T]> { idx.idx(self) } + /// Same as `.get_out()`, but with the bound check being elided. + /// /// # Safety /// - /// The given `idx` mut be in bounds. + /// The given `idx` mut be in bounds: + /// + /// - if `idx: usize`, then `idx` must be `< self.len()`. + /// + /// - if `idx` is an upper-bounded range (_e.g._, `.. b`, `a ..= b`), + /// then the upper bound (`b` in the example) must be `< self.len()`. + /// + /// - _etc_. + /// + /// See [`.get_unchecked_mut()`]( + /// https://doc.rust-lang.org/std/primitive.slice.html#method.get_unchecked_mut) + /// for more info about the safety of such call. #[inline] pub unsafe - fn get_out_unchecked (self: Out<'out, [T]>, idx: Index) + fn get_unchecked_out (self: Out<'out, [T]>, idx: Index) -> Index::Output where - Index : SliceIndex<'out, T>, + Index : UsizeOrRange<'out, T>, // renamed for the documentation { self.get_out(idx) .unwrap_or_else(|| if cfg!(debug_assertions) { @@ -379,6 +545,18 @@ impl<'out, T : 'out> Out<'out, [T]> { }) } + + /// Upgrades the `&out [_]` (write-only) reference to a read-writeable + /// `&mut [_]`. + /// + /// # Safety + /// + /// Don't be lured by the `&mut` reference: Rust validity invariants + /// imply that an `&mut` reference is only sound to produce if it points + /// to initialized values; it is otherwise instant UB. See + /// [`MaybeUninit::assume_init`] for more info about it. Thus: + /// + /// - The pointee(s) must have been initialized. #[inline] pub unsafe @@ -402,7 +580,7 @@ impl<'out, T : 'out> Out<'out, [T]> { /// /// The function panics if the slices' lengths are not equal. /// - /// # Guarantees + /// # Guarantees (that `unsafe` code may rely on) /// /// A non-`panic!`king return from this function **guarantees that the input /// slice has been (successfully) initialized**, and that it is thus then @@ -451,7 +629,7 @@ impl<'out, T : 'out> Out<'out, [T]> { // - the `copy_nonoverlapping()` call guarantees that the buffer // has been initialized. self.r() - .as_uninit_mut() + .as_mut_uninit() .copy_from_slice( <[MaybeUninit]>::from_ref(source_slice) ) @@ -460,25 +638,35 @@ impl<'out, T : 'out> Out<'out, [T]> { } } - /// Fill the buffer with values from a function mapping indices to values. + /// Fills the buffer with values from up to the first `self.len()` elements + /// of an `iterable`. /// - /// # Guarantees + /// # Guarantees (that `unsafe` code may rely on) /// - /// `factory` is guaranteed to be called in the `0 .. self.len()` order. + /// A non-panicking return from this function guarantees that the first `k` + /// values of the buffer have been initialized and are thus sound to + /// `.assume_init()`, where `k`, the numbers of elements that `iterable` + /// has yielded (capped at `self.len()`), is the length of the returned + /// buffer. #[inline] pub - fn fill_with ( + fn init_with ( mut self: Out<'out, [T]>, - mut factory: impl FnMut(usize) -> T, + iterable: impl IntoIterator, ) -> &'out mut [T] { - self.iter_out() - .enumerate() - .for_each(|(i, at_dst)| { at_dst.write(factory(i)); }) + let len = self.len(); + let mut iter_out = self.iter_out(); + iter_out + .by_ref() + .zip(iterable) + .for_each(|(at_dst, next)| { at_dst.write(next); }) ; + let init_count = len - iter_out.remaining().len(); unsafe { - // Safety: The `len` values of the buffer have been initialized - self.assume_init() + // Safety: `init_count` values of the buffer have been initialized + self.get_unchecked_out(.. init_count) + .assume_init() } } @@ -488,15 +676,21 @@ impl<'out, T : 'out> Out<'out, [T]> { fn iter_out<'reborrow> (self: &'reborrow mut Out<'out, [T]>) -> iter::IterOut<'reborrow, T> { - self.r().into_iter() + self.into_iter() } + /// Same as [`.split_at_mut()`](https://doc.rust-lang.org/std/primitive.slice.html#method.split_at_mut + /// ), but with `&out [_]` references. + /// + /// # Panic + /// + /// Panics if `idx > len`. #[inline] pub fn split_at_out (self: Out<'out, [T]>, idx: usize) -> (Out<'out, [T]>, Out<'out, [T]> ) { - let (left, right) = unsafe { self.as_uninit_mut() }.split_at_mut(idx); + let (left, right) = unsafe { self.as_mut_uninit() }.split_at_mut(idx); (left.into(), right.into()) } } @@ -516,7 +710,7 @@ impl<'out, T : 'out> ::core::ops::Deref for Out<'out, [T]> { } } -use private::SliceIndex; +use private::{SliceIndex, SliceIndex as UsizeOrRange}; mod private { use super::*; @@ -530,17 +724,30 @@ mod private { } } +/// `&out [_]` slice iteration logic. pub mod iter { use super::*; + /// The value obtained when calling `.into_iter()` on a `Out<'out, [T]>`. + /// + /// An iterator over single value `Out<'out, T>` references. + #[allow(missing_debug_implementations)] pub struct IterOut<'out, T : 'out> { - /// Points to the remaining elements of the iterator. - pub slice: Out<'out, [T]>, + } - _non_exhaustive: (), + impl<'out, T : 'out> IterOut<'out, T> { + /// Extracts an `Out<[T]>` slice reference pointing to the elements not + /// yet yielded by the iterator. + #[inline] + pub + fn remaining (self: IterOut<'out, T>) + -> Out<'out, [T]> + { + self.slice + } } impl<'out, T : 'out> IntoIterator for Out<'out, [T]> { @@ -550,7 +757,21 @@ mod iter { fn into_iter (self: Out<'out, [T]>) -> IterOut<'out, T> { - IterOut { slice: self, _non_exhaustive: () } + IterOut { slice: self } + } + } + + impl<'out, 'inner : 'out, T : 'inner> IntoIterator + for &'out mut Out<'inner, [T]> + { + type Item = Out<'out, T>; + type IntoIter = IterOut<'out, T>; + + #[inline] + fn into_iter (self: &'out mut Out<'inner, [T]>) + -> IterOut<'out, T> + { + self.reborrow().into_iter() } } @@ -566,7 +787,7 @@ mod iter { let (first, rest) = slice.split_at_out(1); self.slice = rest; Some(unsafe { - first.get_out_unchecked(0) + first.get_unchecked_out(0) }) } } diff --git a/src/read/impls.rs b/src/read/impls.rs index 345d9b9..bbf3a67 100644 --- a/src/read/impls.rs +++ b/src/read/impls.rs @@ -24,6 +24,7 @@ impl ReadIntoUninit for &'_ mut R { // - `read_into_uninit_exact` is not overriden unsafe impl ReadIntoUninit for &'_ [u8] { + #[inline] fn read_into_uninit<'buf> ( self: &'_ mut Self, buf: Out<'buf, [u8]>, diff --git a/src/read/mod.rs b/src/read/mod.rs index 51cb5e3..ccaf74c 100644 --- a/src/read/mod.rs +++ b/src/read/mod.rs @@ -1,3 +1,5 @@ +//! Read into uninitialized bytes logic. + use_prelude!(); use ::std::io::Read; @@ -88,6 +90,7 @@ trait ReadIntoUninit : Read { }) } + /// Chains / concats two `ReadIntoUninit` readers into one. #[cfg(feature = "chain")] fn chain ( self: Self, @@ -128,14 +131,16 @@ macro_rules! auto_impl {( #[doc(cfg(feature = "specialization"))] default unsafe impl ReadIntoUninit for R { + #[inline] default fn read_into_uninit<'buf> ( self: &'_ mut Self, buf: Out<'buf, [u8]>, ) -> io::Result<&'buf mut [u8]> { - let buf = buf.fill_with(|_| 0); - self.read(buf).map(move |n| &mut buf[.. n]) + let buf = buf.init_with(::core::iter::repeat(0)); + self.read(buf) + .map(move |n| &mut buf[.. n]) } #[inline] @@ -145,8 +150,9 @@ unsafe impl ReadIntoUninit for R { buf: Out<'buf, [u8]>, ) -> io::Result<&'buf mut [u8]> { - let buf = buf.fill_with(|_| 0); - self.read_exact(buf).map(|()| buf) + let buf = buf.init_with(::core::iter::repeat(0)); + self.read_exact(buf) + .map(|()| buf) } } diff --git a/src/utils/macros.rs b/src/utils/macros.rs index 261317d..e318351 100644 --- a/src/utils/macros.rs +++ b/src/utils/macros.rs @@ -1,11 +1,13 @@ macro_rules! pub_in_crate {( $( + $(#[$meta:meta])* use $($first:ident)? $(:: $extra:ident)* :: { $($tree:tt)* }; )* ) => ( $( + $(#[$meta])* pub(in crate) use $($first)? $(:: $extra)* :: { $($tree)* @@ -16,3 +18,15 @@ macro_rules! pub_in_crate {( macro_rules! use_prelude {() => ( pub(in crate) use crate::utils::prelude::*; )} + +macro_rules! cfg_std {( + $($item:item)* +) => ( + $( + #[cfg(feature = "std")] + #[cfg_attr(feature = "nightly", + doc(cfg(feature = "std")), + )] + $item + )* +)} diff --git a/src/utils/prelude.rs b/src/utils/prelude.rs index c20e5b5..dcdafa2 100644 --- a/src/utils/prelude.rs +++ b/src/utils/prelude.rs @@ -3,18 +3,31 @@ #![allow(unused_imports)] pub_in_crate! { - use std::{*, + use ::core::{ convert::{ TryFrom, TryInto, }, - mem::MaybeUninit, + hint, + mem::{self, + MaybeUninit, + }, + ptr, ops::{ Not, }, + slice, + }; + #[cfg(feature = "std")] + use ::std::{ + alloc, + io, }; use crate::{ prelude::*, + }; + #[cfg(feature = "std")] + use crate::{ extension_traits::{ VecExtendFromReader, },