diff --git a/src/de/impl_core.rs b/src/de/impl_core.rs deleted file mode 100644 index 0478f83f..00000000 --- a/src/de/impl_core.rs +++ /dev/null @@ -1,186 +0,0 @@ -#![allow(unused_unsafe)] - -//! Contains implementations for rust core that have not been stabilized -//! -//! Functions in this are expected to be properly peer reviewed by the community -//! -//! Any modifications done are purely to make the code compatible with bincode - -use core::mem::{self, MaybeUninit}; - -/// Pulls `N` items from `iter` and returns them as an array. If the iterator -/// yields fewer than `N` items, `None` is returned and all already yielded -/// items are dropped. -/// -/// Since the iterator is passed as a mutable reference and this function calls -/// `next` at most `N` times, the iterator can still be used afterwards to -/// retrieve the remaining items. -/// -/// If `iter.next()` panicks, all items already yielded by the iterator are -/// dropped. -#[allow(clippy::while_let_on_iterator)] -pub fn collect_into_array(iter: &mut I) -> Option> -where - I: Iterator>, -{ - if N == 0 { - // SAFETY: An empty array is always inhabited and has no validity invariants. - return unsafe { Some(Ok(mem::zeroed())) }; - } - - struct Guard<'a, T, const N: usize> { - array_mut: &'a mut [MaybeUninit; N], - initialized: usize, - } - - impl Drop for Guard<'_, T, N> { - fn drop(&mut self) { - debug_assert!(self.initialized <= N); - - // SAFETY: this slice will contain only initialized objects. - unsafe { - core::ptr::drop_in_place(slice_assume_init_mut( - self.array_mut.get_unchecked_mut(..self.initialized), - )); - } - } - } - - let mut array = uninit_array::(); - let mut guard = Guard { - array_mut: &mut array, - initialized: 0, - }; - - while let Some(item_rslt) = iter.next() { - let item = match item_rslt { - Err(err) => { - return Some(Err(err)); - } - Ok(elem) => elem, - }; - - // SAFETY: `guard.initialized` starts at 0, is increased by one in the - // loop and the loop is aborted once it reaches N (which is - // `array.len()`). - unsafe { - guard - .array_mut - .get_unchecked_mut(guard.initialized) - .write(item); - } - guard.initialized += 1; - - // Check if the whole array was initialized. - if guard.initialized == N { - mem::forget(guard); - - // SAFETY: the condition above asserts that all elements are - // initialized. - let out = unsafe { array_assume_init(array) }; - return Some(Ok(out)); - } - } - - // This is only reached if the iterator is exhausted before - // `guard.initialized` reaches `N`. Also note that `guard` is dropped here, - // dropping all already initialized elements. - None -} - -/// Assuming all the elements are initialized, get a mutable slice to them. -/// -/// # Safety -/// -/// It is up to the caller to guarantee that the `MaybeUninit` elements -/// really are in an initialized state. -/// Calling this when the content is not yet fully initialized causes undefined behavior. -/// -/// See [`assume_init_mut`] for more details and examples. -/// -/// [`assume_init_mut`]: MaybeUninit::assume_init_mut -// #[unstable(feature = "maybe_uninit_slice", issue = "63569")] -// #[rustc_const_unstable(feature = "const_maybe_uninit_assume_init", issue = "none")] -#[inline(always)] -pub unsafe fn slice_assume_init_mut(slice: &mut [MaybeUninit]) -> &mut [T] { - // SAFETY: similar to safety notes for `slice_get_ref`, but we have a - // mutable reference which is also guaranteed to be valid for writes. - unsafe { &mut *(slice as *mut [MaybeUninit] as *mut [T]) } -} - -/// Create a new array of `MaybeUninit` items, in an uninitialized state. -/// -/// Note: in a future Rust version this method may become unnecessary -/// when Rust allows -/// [inline const expressions](https://github.com/rust-lang/rust/issues/76001). -/// The example below could then use `let mut buf = [const { MaybeUninit::::uninit() }; 32];`. -/// -/// # Examples -/// -/// ```ignore -/// #![feature(maybe_uninit_uninit_array, maybe_uninit_extra, maybe_uninit_slice)] -/// -/// use std::mem::MaybeUninit; -/// -/// extern "C" { -/// fn read_into_buffer(ptr: *mut u8, max_len: usize) -> usize; -/// } -/// -/// /// Returns a (possibly smaller) slice of data that was actually read -/// fn read(buf: &mut [MaybeUninit]) -> &[u8] { -/// unsafe { -/// let len = read_into_buffer(buf.as_mut_ptr() as *mut u8, buf.len()); -/// MaybeUninit::slice_assume_init_ref(&buf[..len]) -/// } -/// } -/// -/// let mut buf: [MaybeUninit; 32] = MaybeUninit::uninit_array(); -/// let data = read(&mut buf); -/// ``` -// #[unstable(feature = "maybe_uninit_uninit_array", issue = "none")] -// #[rustc_const_unstable(feature = "maybe_uninit_uninit_array", issue = "none")] -#[inline(always)] -fn uninit_array() -> [MaybeUninit; LEN] { - // SAFETY: An uninitialized `[MaybeUninit<_>; LEN]` is valid. - unsafe { MaybeUninit::<[MaybeUninit; LEN]>::uninit().assume_init() } -} - -/// Extracts the values from an array of `MaybeUninit` containers. -/// -/// # Safety -/// -/// It is up to the caller to guarantee that all elements of the array are -/// in an initialized state. -/// -/// # Examples -/// -/// ```ignore -/// #![feature(maybe_uninit_uninit_array)] -/// #![feature(maybe_uninit_array_assume_init)] -/// use std::mem::MaybeUninit; -/// -/// let mut array: [MaybeUninit; 3] = MaybeUninit::uninit_array(); -/// array[0].write(0); -/// array[1].write(1); -/// array[2].write(2); -/// -/// // SAFETY: Now safe as we initialised all elements -/// let array = unsafe { -/// MaybeUninit::array_assume_init(array) -/// }; -/// -/// assert_eq!(array, [0, 1, 2]); -/// ``` -// #[unstable(feature = "maybe_uninit_array_assume_init", issue = "80908")] -#[inline(always)] -pub unsafe fn array_assume_init(array: [MaybeUninit; N]) -> [T; N] { - // SAFETY: - // * The caller guarantees that all elements of the array are initialized - // * `MaybeUninit` and T are guaranteed to have the same layout - // * `MaybeUninit` does not drop, so there are no double-frees - // And thus the conversion is safe - unsafe { - // intrinsics::assert_inhabited::<[T; N]>(); - (&array as *const _ as *const [T; N]).read() - } -} diff --git a/src/de/impls.rs b/src/de/impls.rs index b7dfecca..fbda6f14 100644 --- a/src/de/impls.rs +++ b/src/de/impls.rs @@ -466,15 +466,15 @@ where let res = unsafe { ptr.read() }; Ok(res) } else { - let result = super::impl_core::collect_into_array(&mut (0..N).map(|_| { + let result = try_collect_into_array(&mut (0..N).map(|_| { // See the documentation on `unclaim_bytes_read` as to why we're doing this here decoder.unclaim_bytes_read(core::mem::size_of::()); T::decode(decoder) - })); + }))?; // result is only None if N does not match the values of `(0..N)`, which it always should // So this unwrap should never occur - result.unwrap() + Ok(unsafe { result.unwrap_unchecked() }) } } } @@ -626,3 +626,70 @@ const UTF8_CHAR_WIDTH: [u8; 256] = [ const fn utf8_char_width(b: u8) -> usize { UTF8_CHAR_WIDTH[b as usize] as usize } + +// Taken from https://github.com/rust-lang/rust/blob/06a1c14d52a8482a33416c21b320970cab80cccc/library/core/src/array/mod.rs#L792 +// and slightly modified +fn try_collect_into_array(iter: &mut I) -> Result, E> +where + I: Iterator>, +{ + use core::mem::MaybeUninit; + + if N == 0 { + // SAFETY: An empty array is always inhabited and has no validity invariants. + return unsafe { Ok(Some(core::mem::zeroed())) }; + } + + struct Guard<'a, T, const N: usize> { + array_mut: &'a mut [MaybeUninit; N], + initialized: usize, + } + + impl Drop for Guard<'_, T, N> { + fn drop(&mut self) { + debug_assert!(self.initialized <= N); + + // SAFETY: this slice will contain only initialized objects. + unsafe { + core::ptr::drop_in_place(MaybeUninit::slice_assume_init_mut( + self.array_mut.get_unchecked_mut(..self.initialized), + )); + } + } + } + + let mut array = MaybeUninit::uninit_array::(); + let mut guard = Guard { + array_mut: &mut array, + initialized: 0, + }; + + for item in iter { + let item = item?; + // SAFETY: `guard.initialized` starts at 0, is increased by one in the + // loop and the loop is aborted once it reaches N (which is + // `array.len()`). + unsafe { + guard + .array_mut + .get_unchecked_mut(guard.initialized) + .write(item); + } + guard.initialized += 1; + + // Check if the whole array was initialized. + if guard.initialized == N { + core::mem::forget(guard); + + // SAFETY: the condition above asserts that all elements are + // initialized. + let out = unsafe { MaybeUninit::array_assume_init(array) }; + return Ok(Some(out)); + } + } + + // This is only reached if the iterator is exhausted before + // `guard.initialized` reaches `N`. Also note that `guard` is dropped here, + // dropping all already initialized elements. + Ok(None) +} diff --git a/src/de/mod.rs b/src/de/mod.rs index a25d3726..05642bf6 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -1,7 +1,6 @@ //! Decoder-based structs and traits. mod decoder; -mod impl_core; mod impl_tuples; mod impls; diff --git a/src/lib.rs b/src/lib.rs index c3f41283..018c36d1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,11 @@ #![no_std] #![warn(missing_docs, unused_lifetimes)] #![cfg_attr(docsrs, feature(doc_cfg))] +#![feature( + maybe_uninit_slice, + maybe_uninit_uninit_array, + maybe_uninit_array_assume_init +)] //! Bincode is a crate for encoding and decoding using a tiny binary //! serialization strategy. Using it, you can easily go from having