move to no_std + alloc
This commit is contained in:
@@ -42,6 +42,10 @@ clone_on_ref_ptr = "deny"
|
||||
shadow_reuse = "deny"
|
||||
shadow_same = "deny"
|
||||
shadow_unrelated = "deny"
|
||||
# Push dependencies down the stack to core and alloc where possible
|
||||
std_instead_of_core = "deny"
|
||||
std_instead_of_alloc = "deny"
|
||||
alloc_instead_of_core = "deny"
|
||||
|
||||
# The wrappers' private field is deliberately type-erased and therefore cannot
|
||||
# express Send/Sync structurally. Their audited APIs preserve the element bounds
|
||||
|
||||
+7
-5
@@ -1,7 +1,9 @@
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use crate::TypeErasedVec;
|
||||
use std::fmt;
|
||||
use std::marker::PhantomData;
|
||||
use std::mem::ManuallyDrop;
|
||||
use core::fmt;
|
||||
use core::marker::PhantomData;
|
||||
use core::mem::ManuallyDrop;
|
||||
|
||||
/// Provides temporary, typed access to a [`TypeErasedVec`] allocation.
|
||||
///
|
||||
@@ -66,12 +68,12 @@ impl<'vec, T> ContentGuard<'vec, T> {
|
||||
///
|
||||
/// The guard remains usable with an empty, zero-capacity vector.
|
||||
pub fn take(&mut self) -> Vec<T> {
|
||||
let old_erased = std::mem::replace(self.erased, (self.reerase)(Vec::new()));
|
||||
let old_erased = core::mem::replace(self.erased, (self.reerase)(Vec::new()));
|
||||
let erased_owner = ManuallyDrop::new(old_erased);
|
||||
|
||||
// SAFETY: ManuallyDrop prevents the old TypeErasedVec from running its
|
||||
// destructor. This reads its uniquely owned descriptor exactly once.
|
||||
let parts = unsafe { std::ptr::read(&raw const erased_owner.parts) };
|
||||
let parts = unsafe { core::ptr::read(&raw const erased_owner.parts) };
|
||||
|
||||
// SAFETY: The erased pointer, length, and capacity are valid for Vec<T>,
|
||||
// and `parts` uniquely owns the allocation.
|
||||
|
||||
+15
-9
@@ -1,4 +1,9 @@
|
||||
#![doc = include_str!("../README.md")]
|
||||
//
|
||||
// We only use core and `alloc::vec::Vec` so we are no_std + alloc compatible
|
||||
// except for in tests where we need to spawn threads to confirm the sync handling
|
||||
#![cfg_attr(not(test), no_std)]
|
||||
extern crate alloc;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
@@ -8,11 +13,12 @@ mod parts;
|
||||
mod send;
|
||||
mod vtable;
|
||||
|
||||
use alloc::vec::Vec;
|
||||
pub use guard::ContentGuard;
|
||||
pub use send::{SendSyncTypeErasedVec, SendTypeErasedVec, SendableTypeErasedVec};
|
||||
|
||||
use std::alloc::Layout;
|
||||
use std::fmt;
|
||||
use core::alloc::Layout;
|
||||
use core::fmt;
|
||||
use vtable::TypeErasedVecVtable;
|
||||
|
||||
use crate::parts::VecParts;
|
||||
@@ -100,7 +106,7 @@ impl TypeErasedVec {
|
||||
|
||||
fn new_scoped<T>(vec: Vec<T>) -> Self {
|
||||
debug_assert!(
|
||||
!std::mem::needs_drop::<T>(),
|
||||
!core::mem::needs_drop::<T>(),
|
||||
"scoped element types must not require drop"
|
||||
);
|
||||
|
||||
@@ -109,7 +115,7 @@ impl TypeErasedVec {
|
||||
layout: Layout::new::<T>(),
|
||||
// Treat the elements as possibly invalid bytes whenever they are
|
||||
// outside a ContentGuard carrying their actual lifetime.
|
||||
vtable: TypeErasedVecVtable::new::<std::mem::MaybeUninit<T>>(),
|
||||
vtable: TypeErasedVecVtable::new::<core::mem::MaybeUninit<T>>(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -223,7 +229,7 @@ impl TypeErasedVec {
|
||||
panic!(
|
||||
"Target type layout must exactly match the erased layout. Capacity is reserved for {:?} but {} has {:?}",
|
||||
layout,
|
||||
std::any::type_name::<T>(),
|
||||
core::any::type_name::<T>(),
|
||||
Layout::new::<T>()
|
||||
)
|
||||
})
|
||||
@@ -240,7 +246,7 @@ impl TypeErasedVec {
|
||||
/// Panics if `T` needs drop.
|
||||
pub fn try_as_type_scoped<T>(&mut self) -> Option<ContentGuard<'_, T>> {
|
||||
assert!(
|
||||
!std::mem::needs_drop::<T>(),
|
||||
!core::mem::needs_drop::<T>(),
|
||||
"scoped element types must not require drop"
|
||||
);
|
||||
|
||||
@@ -249,7 +255,7 @@ impl TypeErasedVec {
|
||||
}
|
||||
|
||||
self.clear();
|
||||
self.vtable = TypeErasedVecVtable::new::<std::mem::MaybeUninit<T>>();
|
||||
self.vtable = TypeErasedVecVtable::new::<core::mem::MaybeUninit<T>>();
|
||||
Some(ContentGuard::new_scoped(self))
|
||||
}
|
||||
|
||||
@@ -272,7 +278,7 @@ impl TypeErasedVec {
|
||||
panic!(
|
||||
"Target type layout must exactly match the erased layout. Capacity is reserved for {:?} but {} has {:?}",
|
||||
layout,
|
||||
std::any::type_name::<T>(),
|
||||
core::any::type_name::<T>(),
|
||||
Layout::new::<T>()
|
||||
)
|
||||
})
|
||||
@@ -291,7 +297,7 @@ impl TypeErasedVec {
|
||||
res.is_some(),
|
||||
"Calling `as_type_unchecked` with an incompatible layout is UB! Target type layout must exactly match the erased layout. Capacity is reserved for {:?} but {} has {:?}",
|
||||
layout,
|
||||
std::any::type_name::<T>(),
|
||||
core::any::type_name::<T>(),
|
||||
Layout::new::<T>()
|
||||
);
|
||||
|
||||
|
||||
+6
-4
@@ -1,5 +1,7 @@
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::ptr::NonNull;
|
||||
use core::mem::ManuallyDrop;
|
||||
use core::ptr::NonNull;
|
||||
|
||||
use alloc::vec::Vec;
|
||||
|
||||
/// A linear ownership descriptor for a type-erased `Vec` allocation.
|
||||
///
|
||||
@@ -52,7 +54,7 @@ impl VecParts {
|
||||
// SAFETY: The VecParts invariant guarantees a live allocation and
|
||||
// initialized length. The caller selects the exact erased type T and
|
||||
// guarantees no mutable access overlaps the returned shared borrow.
|
||||
unsafe { std::slice::from_raw_parts(self.ptr.as_ptr().cast(), self.len) }
|
||||
unsafe { core::slice::from_raw_parts(self.ptr.as_ptr().cast(), self.len) }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
@@ -60,6 +62,6 @@ impl VecParts {
|
||||
// SAFETY: The VecParts invariant guarantees a live allocation and
|
||||
// initialized length. The caller selects the exact erased type T and
|
||||
// guarantees unique access with no overlapping references.
|
||||
unsafe { std::slice::from_raw_parts_mut(self.ptr.as_ptr().cast(), self.len) }
|
||||
unsafe { core::slice::from_raw_parts_mut(self.ptr.as_ptr().cast(), self.len) }
|
||||
}
|
||||
}
|
||||
|
||||
+3
-2
@@ -1,6 +1,7 @@
|
||||
use crate::{ContentGuard, TypeErasedVec};
|
||||
use std::alloc::Layout;
|
||||
use std::fmt;
|
||||
use alloc::vec::Vec;
|
||||
use core::alloc::Layout;
|
||||
use core::fmt;
|
||||
|
||||
macro_rules! define_thread_safe_erased_vec {
|
||||
(
|
||||
|
||||
+20
-17
@@ -3,15 +3,18 @@
|
||||
reason = "panic paths and panic assertions are intentional test behavior"
|
||||
)]
|
||||
|
||||
use std::panic::catch_unwind;
|
||||
|
||||
use alloc::rc::Rc;
|
||||
use alloc::sync::Arc;
|
||||
use alloc::{format, vec};
|
||||
|
||||
use super::*;
|
||||
use std::alloc::Layout;
|
||||
use std::cell::Cell;
|
||||
use std::marker::PhantomData;
|
||||
use std::mem::{align_of, size_of};
|
||||
use std::panic;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use core::cell::Cell;
|
||||
use core::marker::PhantomData;
|
||||
use core::mem::{align_of, size_of};
|
||||
use core::panic;
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
struct DropTracker {
|
||||
counter: Rc<Cell<usize>>,
|
||||
@@ -172,14 +175,14 @@ macro_rules! assert_thread_safe_erased_vec_api {
|
||||
|
||||
assert!(erased.try_as_type::<u8>().is_none());
|
||||
assert!(
|
||||
panic::catch_unwind(panic::AssertUnwindSafe(|| {
|
||||
catch_unwind(panic::AssertUnwindSafe(|| {
|
||||
let _ = erased.as_type::<u8>();
|
||||
}))
|
||||
.is_err()
|
||||
);
|
||||
assert!(erased.try_as_type_scoped::<u8>().is_none());
|
||||
assert!(
|
||||
panic::catch_unwind(panic::AssertUnwindSafe(|| {
|
||||
catch_unwind(panic::AssertUnwindSafe(|| {
|
||||
let _ = erased.as_type_scoped::<u8>();
|
||||
}))
|
||||
.is_err()
|
||||
@@ -310,7 +313,7 @@ fn test_reserve_is_unwind_safe() {
|
||||
let original_capacity = values.capacity();
|
||||
let mut erased = TypeErasedVec::new(values);
|
||||
|
||||
let result = panic::catch_unwind(panic::AssertUnwindSafe(|| {
|
||||
let result = catch_unwind(panic::AssertUnwindSafe(|| {
|
||||
erased.reserve(usize::MAX);
|
||||
}));
|
||||
|
||||
@@ -369,7 +372,7 @@ fn test_clear_is_unwind_safe_when_element_drop_panics() {
|
||||
let original_capacity = values.capacity();
|
||||
let mut erased = TypeErasedVec::new(values);
|
||||
|
||||
let result = panic::catch_unwind(panic::AssertUnwindSafe(|| {
|
||||
let result = catch_unwind(panic::AssertUnwindSafe(|| {
|
||||
erased.clear();
|
||||
}));
|
||||
|
||||
@@ -478,7 +481,7 @@ fn test_as_type_mismatch_panics_before_mutation() {
|
||||
let original_capacity = values.capacity();
|
||||
let mut erased = TypeErasedVec::new(values);
|
||||
|
||||
let result = panic::catch_unwind(panic::AssertUnwindSafe(|| {
|
||||
let result = catch_unwind(panic::AssertUnwindSafe(|| {
|
||||
let _ = erased.as_type::<u8>();
|
||||
}));
|
||||
|
||||
@@ -508,7 +511,7 @@ fn test_guard_with_unwind_safety() {
|
||||
let mut erased = TypeErasedVec::new(vec);
|
||||
assert_erased_state(&erased, 1, Layout::new::<DropTracker>());
|
||||
|
||||
let res = panic::catch_unwind(panic::AssertUnwindSafe(|| {
|
||||
let res = catch_unwind(panic::AssertUnwindSafe(|| {
|
||||
let mut guard = unsafe { erased.cast_type::<DropTracker>() };
|
||||
|
||||
guard.with(|v| {
|
||||
@@ -904,7 +907,7 @@ fn test_scoped_guard_restores_after_panic_and_clears_stale_bytes() {
|
||||
retained_ptr = guard.as_slice().as_ptr();
|
||||
retained_capacity = guard.capacity();
|
||||
|
||||
let result = panic::catch_unwind(panic::AssertUnwindSafe(|| {
|
||||
let result = catch_unwind(panic::AssertUnwindSafe(|| {
|
||||
guard.with(|values| {
|
||||
values.push(&first);
|
||||
values.push(&second);
|
||||
@@ -1000,7 +1003,7 @@ fn test_cast_and_clear() {
|
||||
fn test_scoped_type_rejects_types_that_need_drop() {
|
||||
let mut erased = TypeErasedVec::new(Vec::<String>::new());
|
||||
|
||||
let result = panic::catch_unwind(panic::AssertUnwindSafe(|| {
|
||||
let result = catch_unwind(panic::AssertUnwindSafe(|| {
|
||||
let _ = erased.as_type_scoped::<String>();
|
||||
}));
|
||||
|
||||
@@ -1050,7 +1053,7 @@ fn test_as_type_scoped_mismatch_panics_before_mutation() {
|
||||
let original_capacity = values.capacity();
|
||||
let mut erased = TypeErasedVec::new(values);
|
||||
|
||||
let result = panic::catch_unwind(panic::AssertUnwindSafe(|| {
|
||||
let result = catch_unwind(panic::AssertUnwindSafe(|| {
|
||||
let _ = erased.as_type_scoped::<u8>();
|
||||
}));
|
||||
|
||||
|
||||
+6
-4
@@ -1,5 +1,7 @@
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use crate::VecParts;
|
||||
use std::mem::ManuallyDrop;
|
||||
use core::mem::ManuallyDrop;
|
||||
|
||||
/// Temporarily reconstructs a typed vector from erased raw parts and writes its
|
||||
/// latest raw parts back when dropped.
|
||||
@@ -13,7 +15,7 @@ struct VecPartsRestoreGuard<'parts, T> {
|
||||
|
||||
impl<'parts, T> VecPartsRestoreGuard<'parts, T> {
|
||||
unsafe fn new(parts: &'parts mut VecParts) -> Self {
|
||||
let original_parts = std::mem::replace(parts, VecParts::from_vec(Vec::<T>::new()));
|
||||
let original_parts = core::mem::replace(parts, VecParts::from_vec(Vec::<T>::new()));
|
||||
|
||||
// SAFETY: The VecParts invariant guarantees global-allocator origin,
|
||||
// valid initialized length and capacity, unique ownership, and single
|
||||
@@ -65,7 +67,7 @@ pub(super) struct TypeErasedVecVtable {
|
||||
impl TypeErasedVecVtable {
|
||||
pub(super) fn new<T>() -> Self {
|
||||
unsafe fn drop_vec<T>(parts: &mut VecParts) {
|
||||
let original_parts = std::mem::replace(parts, VecParts::from_vec(Vec::<T>::new()));
|
||||
let original_parts = core::mem::replace(parts, VecParts::from_vec(Vec::<T>::new()));
|
||||
|
||||
// SAFETY: The vtable selects T as the exact type that originally
|
||||
// produced these uniquely owned raw parts. Reconstructing the Vec
|
||||
@@ -81,7 +83,7 @@ impl TypeErasedVecVtable {
|
||||
// were produced by Vec<T>.
|
||||
let mut restore_guard = unsafe { VecPartsRestoreGuard::<T>::new(parts) };
|
||||
restore_guard.vec_mut().clear();
|
||||
debug_assert!(std::ptr::eq(
|
||||
debug_assert!(core::ptr::eq(
|
||||
restore_guard.pointer().cast::<u8>(),
|
||||
original_ptr
|
||||
));
|
||||
|
||||
Reference in New Issue
Block a user