Skip to content

Commit

Permalink
Replace unstable std lib functions with local versions
Browse files Browse the repository at this point in the history
**Description**
Work towards issue #20

For each unstable standard library function we depend on, copy the
implementation and documentation into the local crate and replace
all usages of it.

Added links to tracking issues so we can eventually swap back.

**Motivation**
This change is needed so that this crate can compile using the
stable rust distribution.

The downside to this change is that we will no longer receive fixes
or other patches that the stdlib does on their version. If they
uncover an issue, we won't know about it. Going to mitigate this
by subscribing to each tracking issue.

**Testing Done**
 - `cargo test`
 - `cargo miri test`
 - `cargo clippy`
  • Loading branch information
declanvk committed Nov 17, 2022
1 parent 7664c16 commit 2061149
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 25 deletions.
12 changes: 4 additions & 8 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,5 @@
#![feature(
maybe_uninit_uninit_array,
maybe_uninit_slice,
nonnull_slice_from_raw_parts,
slice_ptr_get,
strict_provenance,
maybe_uninit_write_slice
)]
// TODO(#20): Use rust stable distribution, remove usage of nightly features
#![feature(slice_ptr_get, strict_provenance)]
#![deny(
missing_docs,
clippy::missing_safety_doc,
Expand Down Expand Up @@ -33,3 +27,5 @@ pub mod tagged_pointer;
pub mod tests_common;

pub use nodes::*;

mod nightly_rust_apis;
101 changes: 101 additions & 0 deletions src/nightly_rust_apis.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
//! Module containing copies of Rust standard library unstable functions for use
//! outside of the nightly distribution.
use std::{
mem::MaybeUninit,
ptr::{slice_from_raw_parts_mut, NonNull},
};

/// Assuming all the elements are initialized, get a slice to them.
///
/// # Safety
///
/// It is up to the caller to guarantee that the `MaybeUninit<T>` elements
/// really are in an initialized state.
/// Calling this when the content is not yet fully initialized causes
/// undefined behavior.
///
/// See [`assume_init_ref`] for more details and examples.
///
/// [`assume_init_ref`]: MaybeUninit::assume_init_ref
///
/// **This is a unstable API copied from the Rust standard library, tracking
/// issue is [#63569][issue-63569]**
///
/// [issue-63569]: https://github.com/rust-lang/rust/issues/63569
pub const unsafe fn maybe_uninit_slice_assume_init_ref<T>(slice: &[MaybeUninit<T>]) -> &[T] {
// SAFETY: casting `slice` to a `*const [T]` is safe since the caller guarantees
// that `slice` is initialized, and `MaybeUninit` is guaranteed to have
// the same layout as `T`. The pointer obtained is valid since it refers
// to memory owned by `slice` which is a reference and thus guaranteed
// to be valid for reads.
unsafe { &*(slice as *const [MaybeUninit<T>] as *const [T]) }
}

/// Create a new array of `MaybeUninit<T>` 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::<u8>::uninit() }; 32];`.
///
/// **This is a unstable API copied from the Rust standard library, tracking
/// issue is [#96097][issue-96097]**
///
/// [issue-96097]: https://github.com/rust-lang/rust/issues/96097
pub const fn maybe_uninit_uninit_array<const N: usize, T>() -> [MaybeUninit<T>; N] {
// SAFETY: An uninitialized `[MaybeUninit<_>; LEN]` is valid.
unsafe { MaybeUninit::<[MaybeUninit<T>; N]>::uninit().assume_init() }
}

/// Assuming all the elements are initialized, get a mutable slice to them.
///
/// # Safety
///
/// It is up to the caller to guarantee that the `MaybeUninit<T>` 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
///
/// **This is a unstable API copied from the Rust standard library, tracking
/// issue is [#63569][issue-63569]**
///
/// [issue-63569]: https://github.com/rust-lang/rust/issues/63569
pub unsafe fn maybe_uninit_slice_assume_init_mut<T>(slice: &mut [MaybeUninit<T>]) -> &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<T>] as *mut [T]) }
}

/// Gets a pointer to the first element of the array.
///
/// **This is a unstable API copied from the Rust standard library, tracking
/// issue is [#63569][issue-63569]**
///
/// [issue-63569]: https://github.com/rust-lang/rust/issues/63569
pub fn maybe_uninit_slice_as_ptr<T>(this: &[MaybeUninit<T>]) -> *const T {
this.as_ptr() as *const T
}

/// Creates a non-null raw slice from a thin pointer and a length.
///
/// The `len` argument is the number of **elements**, not the number of
/// bytes.
///
/// This function is safe, but dereferencing the return value is unsafe.
/// See the documentation of [`std::slice::from_raw_parts`] for slice safety
/// requirements.
///
/// **This is a unstable API copied from the Rust standard library, tracking
/// issue is [#71941][issue-71941]**
///
/// [issue-71941]: https://github.com/rust-lang/rust/issues/71941
pub fn non_null_slice_from_raw_parts<T>(data: NonNull<T>, len: usize) -> NonNull<[T]> {
// SAFETY: `data` is a `NonNull` pointer which is necessarily non-null
unsafe { NonNull::<[T]>::new_unchecked(slice_from_raw_parts_mut(data.as_ptr(), len)) }
}
34 changes: 20 additions & 14 deletions src/nodes/representation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -597,8 +597,8 @@ impl<V, const SIZE: usize> InnerNodeCompressed<V, SIZE> {
pub fn empty() -> Self {
InnerNodeCompressed {
header: Header::default(),
child_pointers: MaybeUninit::uninit_array(),
keys: MaybeUninit::uninit_array(),
child_pointers: crate::nightly_rust_apis::maybe_uninit_uninit_array(),
keys: crate::nightly_rust_apis::maybe_uninit_uninit_array(),
}
}

Expand All @@ -620,8 +620,10 @@ impl<V, const SIZE: usize> InnerNodeCompressed<V, SIZE> {
// be initialized
unsafe {
(
MaybeUninit::slice_assume_init_ref(&self.keys[0..self.header.num_children()]),
MaybeUninit::slice_assume_init_ref(
crate::nightly_rust_apis::maybe_uninit_slice_assume_init_ref(
&self.keys[0..self.header.num_children()],
),
crate::nightly_rust_apis::maybe_uninit_slice_assume_init_ref(
&self.child_pointers[0..self.header.num_children()],
),
)
Expand Down Expand Up @@ -702,8 +704,8 @@ impl<V, const SIZE: usize> InnerNodeCompressed<V, SIZE> {
);

let header = self.header.clone();
let mut keys = MaybeUninit::<u8>::uninit_array::<NEW_SIZE>();
let mut child_pointers = MaybeUninit::<OpaqueNodePtr<V>>::uninit_array::<NEW_SIZE>();
let mut keys = crate::nightly_rust_apis::maybe_uninit_uninit_array();
let mut child_pointers = crate::nightly_rust_apis::maybe_uninit_uninit_array();

keys[..header.num_children()].copy_from_slice(&self.keys[..header.num_children()]);
child_pointers[..header.num_children()]
Expand All @@ -719,7 +721,7 @@ impl<V, const SIZE: usize> InnerNodeCompressed<V, SIZE> {
fn grow_node48(&self) -> InnerNode48<V> {
let header = self.header.clone();
let mut child_indices = [RestrictedNodeIndex::<48>::EMPTY; 256];
let mut child_pointers = MaybeUninit::<OpaqueNodePtr<V>>::uninit_array::<48>();
let mut child_pointers = crate::nightly_rust_apis::maybe_uninit_uninit_array();

let (n16_keys, _) = self.initialized_portion();

Expand Down Expand Up @@ -952,7 +954,7 @@ impl<V> InnerNode48<V> {
InnerNode48 {
header: Header::default(),
child_indices: [RestrictedNodeIndex::<48>::EMPTY; 256],
child_pointers: MaybeUninit::uninit_array(),
child_pointers: crate::nightly_rust_apis::maybe_uninit_uninit_array(),
}
}

Expand All @@ -961,7 +963,9 @@ impl<V> InnerNode48<V> {
// SAFETY: The array prefix with length `header.num_children` is guaranteed to
// be initialized
unsafe {
MaybeUninit::slice_assume_init_ref(&self.child_pointers[0..self.header.num_children()])
crate::nightly_rust_apis::maybe_uninit_slice_assume_init_ref(
&self.child_pointers[0..self.header.num_children()],
)
}
}
}
Expand Down Expand Up @@ -1070,7 +1074,7 @@ impl<V> InnerNode for InnerNode48<V> {

let header = self.header.clone();

let mut key_and_child_ptrs = MaybeUninit::<(u8, OpaqueNodePtr<V>)>::uninit_array::<16>();
let mut key_and_child_ptrs = crate::nightly_rust_apis::maybe_uninit_uninit_array::<16, _>();
// SAFETY: The lifetime of this iterator is bounded to this function, and will
// not overlap with any mutating operations on the node because it of the shared
// reference that this function uses.
Expand All @@ -1085,16 +1089,18 @@ impl<V> InnerNode for InnerNode48<V> {
// array because the previous iterator loops through all children of the inner
// node.
let init_key_and_child_ptrs = unsafe {
MaybeUninit::slice_assume_init_mut(&mut key_and_child_ptrs[..header.num_children()])
crate::nightly_rust_apis::maybe_uninit_slice_assume_init_mut(
&mut key_and_child_ptrs[..header.num_children()],
)
};

init_key_and_child_ptrs.sort_unstable_by_key(|(key_byte, _)| *key_byte);

init_key_and_child_ptrs
};

let mut keys = MaybeUninit::<u8>::uninit_array::<16>();
let mut child_pointers = MaybeUninit::<OpaqueNodePtr<V>>::uninit_array::<16>();
let mut keys = crate::nightly_rust_apis::maybe_uninit_uninit_array();
let mut child_pointers = crate::nightly_rust_apis::maybe_uninit_uninit_array();

for (idx, (key_byte, child_ptr)) in init_key_and_child_ptrs.iter().copied().enumerate() {
keys[idx].write(key_byte);
Expand Down Expand Up @@ -1207,7 +1213,7 @@ impl<V> InnerNode for InnerNode256<V> {

let header = self.header.clone();
let mut child_indices = [RestrictedNodeIndex::<48>::EMPTY; 256];
let mut child_pointers = MaybeUninit::<OpaqueNodePtr<V>>::uninit_array::<48>();
let mut child_pointers = crate::nightly_rust_apis::maybe_uninit_uninit_array();

// SAFETY: This iterator lives only for the lifetime of this function, which
// does not mutate the `InnerNode256` (guaranteed by reference).
Expand Down
10 changes: 7 additions & 3 deletions src/nodes/representation/iterators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,12 @@ impl<V> InnerNodeCompressedIter<V> {
// the value is known to be initialized.
let (keys_start, child_pointers_start) = unsafe {
(
NonNull::new_unchecked(MaybeUninit::slice_as_ptr(&node.keys) as *mut _),
NonNull::new_unchecked(MaybeUninit::slice_as_ptr(&node.child_pointers) as *mut _),
NonNull::new_unchecked(crate::nightly_rust_apis::maybe_uninit_slice_as_ptr(
&node.keys,
) as *mut _),
NonNull::new_unchecked(crate::nightly_rust_apis::maybe_uninit_slice_as_ptr(
&node.child_pointers,
) as *mut _),
)
};

Expand Down Expand Up @@ -392,7 +396,7 @@ impl<V> InnerNode48Iter<V> {
let child_pointers_ptr = {
let child_pointers_slice = node.initialized_child_pointers();

NonNull::slice_from_raw_parts(
crate::nightly_rust_apis::non_null_slice_from_raw_parts(
NonNull::new(child_pointers_slice.as_ptr() as *mut _).unwrap(),
child_pointers_slice.len(),
)
Expand Down

0 comments on commit 2061149

Please sign in to comment.