continue refactor
This commit is contained in:
+2
-9
@@ -60,19 +60,12 @@ impl<'vec, T> ContentGuard<'vec, T> {
|
||||
#[must_use]
|
||||
pub fn as_slice(&self) -> &[T] {
|
||||
// SAFETY: The pointer and length correctly represent the currently initialized elements.
|
||||
unsafe {
|
||||
std::slice::from_raw_parts(self.erased.parts.ptr.as_ptr().cast(), self.erased.parts.len)
|
||||
}
|
||||
unsafe { self.erased.parts.as_slice() }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn as_slice_mut(&mut self) -> &mut [T] {
|
||||
// SAFETY: The pointer and length correctly represent the currently initialized elements.
|
||||
unsafe {
|
||||
std::slice::from_raw_parts_mut(
|
||||
self.erased.parts.ptr.as_ptr().cast(),
|
||||
self.erased.parts.len,
|
||||
)
|
||||
}
|
||||
unsafe { self.erased.parts.as_slice_mut() }
|
||||
}
|
||||
}
|
||||
|
||||
+3
-23
@@ -1,9 +1,10 @@
|
||||
#![warn(clippy::pedantic)]
|
||||
#![warn(clippy::all, clippy::pedantic)]
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
mod guard;
|
||||
mod parts;
|
||||
mod send;
|
||||
mod vtable;
|
||||
|
||||
@@ -11,30 +12,9 @@ pub use guard::ContentGuard;
|
||||
pub use send::SendableTypeErasedVec;
|
||||
|
||||
use std::alloc::Layout;
|
||||
use std::ptr::NonNull;
|
||||
use vtable::TypeErasedVecVtable;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub(crate) struct VecParts {
|
||||
pub ptr: NonNull<u8>,
|
||||
pub len: usize,
|
||||
pub cap: usize,
|
||||
}
|
||||
|
||||
impl VecParts {
|
||||
pub(crate) fn from_vec<T>(vec: Vec<T>) -> Self {
|
||||
let (ptr, len, cap) = vec.into_raw_parts();
|
||||
|
||||
// SAFETY: Vec guarantees its underlying pointer is non-null.
|
||||
let ptr = unsafe { NonNull::new_unchecked(ptr.cast::<u8>()) };
|
||||
|
||||
Self { ptr, len, cap }
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn into_vec<T>(self) -> Vec<T> {
|
||||
unsafe { Vec::from_raw_parts(self.ptr.as_ptr().cast::<T>(), self.len, self.cap) }
|
||||
}
|
||||
}
|
||||
use crate::parts::VecParts;
|
||||
|
||||
/// Stores the capacity of a `Vec<U>` for later reuse as a `Vec<V>` where `V` shares the same `Layout` as `U`.
|
||||
pub struct TypeErasedVec {
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
use std::ptr::NonNull;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub(crate) struct VecParts {
|
||||
pub ptr: NonNull<u8>,
|
||||
pub len: usize,
|
||||
pub cap: usize,
|
||||
}
|
||||
|
||||
impl VecParts {
|
||||
pub(crate) fn from_vec<T>(vec: Vec<T>) -> Self {
|
||||
let (ptr, len, cap) = vec.into_raw_parts();
|
||||
|
||||
// SAFETY: Vec guarantees its underlying pointer is non-null.
|
||||
let ptr = unsafe { NonNull::new_unchecked(ptr.cast::<u8>()) };
|
||||
|
||||
Self { ptr, len, cap }
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn into_vec<T>(self) -> Vec<T> {
|
||||
unsafe { Vec::from_raw_parts(self.ptr.as_ptr().cast::<T>(), self.len, self.cap) }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub(crate) unsafe fn as_slice<T>(&self) -> &[T] {
|
||||
// SAFETY: Ensured by the caller
|
||||
unsafe { std::slice::from_raw_parts(self.ptr.as_ptr().cast(), self.len) }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub(crate) unsafe fn as_slice_mut<T>(&mut self) -> &mut [T] {
|
||||
// SAFETY: Ensured by the caller
|
||||
unsafe { std::slice::from_raw_parts_mut(self.ptr.as_ptr().cast(), self.len) }
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ impl SendableTypeErasedVec {
|
||||
Self(vec)
|
||||
}
|
||||
|
||||
/// Unpackage the inner `TypeErasedVec`.
|
||||
#[must_use]
|
||||
pub fn unpack(self) -> TypeErasedVec {
|
||||
self.0
|
||||
|
||||
+46
-54
@@ -21,15 +21,9 @@ impl Drop for DropTracker {
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn assert_erased_state(
|
||||
erased: &TypeErasedVec,
|
||||
expected_len: usize,
|
||||
expected_min_cap: usize,
|
||||
expected_layout: Layout,
|
||||
) {
|
||||
fn assert_erased_state(erased: &TypeErasedVec, expected_len: usize, expected_layout: Layout) {
|
||||
assert_eq!(erased.length(), expected_len, "Length mismatch");
|
||||
assert!(erased.capacity() >= expected_min_cap, "Capacity too small");
|
||||
assert_eq!(erased.is_empty(), expected_len == 0, "is_empty mismatch");
|
||||
assert_eq!(erased.is_empty(), expected_len == 0, "Is_empty mismatch");
|
||||
assert_eq!(erased.layout(), expected_layout, "Layout mismatch");
|
||||
assert_eq!(
|
||||
erased.capacity_bytes(),
|
||||
@@ -39,16 +33,8 @@ fn assert_erased_state(
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn assert_guard_state<T>(
|
||||
guard: &ContentGuard<'_, T>,
|
||||
expected_len: usize,
|
||||
expected_min_cap: usize,
|
||||
) {
|
||||
fn assert_guard_state<T>(guard: &ContentGuard<'_, T>, expected_len: usize) {
|
||||
assert_eq!(guard.length(), expected_len, "Guard length mismatch");
|
||||
assert!(
|
||||
guard.capacity() >= expected_min_cap,
|
||||
"Guard capacity too small"
|
||||
);
|
||||
assert_eq!(
|
||||
guard.is_empty(),
|
||||
expected_len == 0,
|
||||
@@ -73,7 +59,8 @@ fn test_new_and_basic_properties() {
|
||||
|
||||
let erased = TypeErasedVec::new(vec);
|
||||
|
||||
assert_erased_state(&erased, 5, initial_cap, Layout::new::<u32>());
|
||||
assert_erased_state(&erased, 5, Layout::new::<u32>());
|
||||
assert!(erased.capacity() >= initial_cap);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -85,63 +72,59 @@ fn test_clear_drops_elements() {
|
||||
vec.push(DropTracker::new(drop_count.clone()));
|
||||
}
|
||||
|
||||
let initial_cap = vec.capacity();
|
||||
let mut erased = TypeErasedVec::new(vec);
|
||||
assert_erased_state(&erased, 5, 5, Layout::new::<DropTracker>());
|
||||
assert_erased_state(&erased, 5, Layout::new::<DropTracker>());
|
||||
assert!(erased.capacity() >= initial_cap);
|
||||
assert_eq!(drop_count.get(), 0);
|
||||
|
||||
erased.clear();
|
||||
|
||||
assert_erased_state(&erased, 0, 5, Layout::new::<DropTracker>());
|
||||
assert_erased_state(&erased, 0, Layout::new::<DropTracker>());
|
||||
assert!(erased.capacity() >= initial_cap);
|
||||
assert_eq!(drop_count.get(), 5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_reserve_increases_capacity() {
|
||||
let vec = Vec::<u64>::with_capacity(2);
|
||||
let vec = Vec::<u64>::new();
|
||||
let mut erased = TypeErasedVec::new(vec);
|
||||
|
||||
assert_erased_state(&erased, 0, 2, Layout::new::<u64>());
|
||||
assert_erased_state(&erased, 0, Layout::new::<u64>());
|
||||
|
||||
erased.reserve(100);
|
||||
|
||||
assert_erased_state(&erased, 0, 100, Layout::new::<u64>());
|
||||
assert_erased_state(&erased, 0, Layout::new::<u64>());
|
||||
// The standard library guarantees that `reserve` allocates *at least* the requested capacity.
|
||||
assert!(erased.capacity() >= 100);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_try_cast_type_success_and_mutation() {
|
||||
fn test_cast_type_success_and_mutation() {
|
||||
let mut vec = Vec::<i32>::with_capacity(10);
|
||||
vec.push(-1);
|
||||
vec.push(-2);
|
||||
let initial_cap = vec.capacity();
|
||||
|
||||
let mut erased = TypeErasedVec::new(vec);
|
||||
assert_erased_state(&erased, 2, initial_cap, Layout::new::<i32>());
|
||||
assert_erased_state(&erased, 2, Layout::new::<i32>());
|
||||
assert!(erased.capacity() >= initial_cap);
|
||||
|
||||
let mut guard = unsafe { erased.try_cast_type::<u32>().expect("Layouts match") };
|
||||
assert_guard_state(&guard, 2, initial_cap);
|
||||
let mut guard = unsafe { erased.cast_type::<u32>() };
|
||||
|
||||
assert_guard_state(&guard, 2);
|
||||
assert!(guard.capacity() >= initial_cap);
|
||||
|
||||
let slice = guard.as_slice_mut();
|
||||
slice[0] = 42;
|
||||
|
||||
let slice = guard.as_slice();
|
||||
assert_eq!(slice, &[42, 4294967294]);
|
||||
// Casting -2_i32 into u32 results in u32::MAX - 1 due to two's complement.
|
||||
assert_eq!(slice, &[42, u32::MAX - 1]);
|
||||
|
||||
let restored = guard.take();
|
||||
assert_eq!(restored, vec![42, 4294967294]);
|
||||
assert_guard_state(&guard, 0, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_try_cast_type_failure() {
|
||||
let vec = Vec::<u32>::with_capacity(5);
|
||||
let initial_cap = vec.capacity();
|
||||
let mut erased = TypeErasedVec::new(vec);
|
||||
|
||||
assert_erased_state(&erased, 0, initial_cap, Layout::new::<u32>());
|
||||
|
||||
let guard = unsafe { erased.try_cast_type::<u64>() };
|
||||
assert!(guard.is_none(), "Should fail due to layout mismatch");
|
||||
assert_erased_state(&erased, 0, initial_cap, Layout::new::<u32>());
|
||||
assert_eq!(restored, vec![42, u32::MAX - 1]);
|
||||
assert_guard_state(&guard, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -153,12 +136,16 @@ fn test_try_to_type_clears_elements() {
|
||||
vec.push(DropTracker::new(drop_count.clone()));
|
||||
}
|
||||
|
||||
let initial_cap = vec.capacity();
|
||||
let mut erased = TypeErasedVec::new(vec);
|
||||
assert_erased_state(&erased, 3, 5, Layout::new::<DropTracker>());
|
||||
assert_erased_state(&erased, 3, Layout::new::<DropTracker>());
|
||||
|
||||
let guard = erased.try_to_type::<DropTracker>().expect("Layouts match");
|
||||
let Some(guard) = erased.try_to_type::<DropTracker>() else {
|
||||
panic!("try_to_type failed despite matching layouts");
|
||||
};
|
||||
|
||||
assert_guard_state(&guard, 0, 5);
|
||||
assert_guard_state(&guard, 0);
|
||||
assert!(guard.capacity() >= initial_cap);
|
||||
assert_eq!(drop_count.get(), 3);
|
||||
}
|
||||
|
||||
@@ -169,10 +156,11 @@ fn test_guard_with_unwind_safety() {
|
||||
vec.push(DropTracker::new(drop_count.clone()));
|
||||
|
||||
let mut erased = TypeErasedVec::new(vec);
|
||||
assert_erased_state(&erased, 1, 10, Layout::new::<DropTracker>());
|
||||
assert_erased_state(&erased, 1, Layout::new::<DropTracker>());
|
||||
|
||||
let res = panic::catch_unwind(panic::AssertUnwindSafe(|| {
|
||||
let mut guard = unsafe { erased.try_cast_type::<DropTracker>().unwrap() };
|
||||
let mut guard = unsafe { erased.cast_type::<DropTracker>() };
|
||||
|
||||
guard.with(|v| {
|
||||
v.push(DropTracker::new(drop_count.clone()));
|
||||
panic!("Simulated panic inside with()");
|
||||
@@ -181,7 +169,7 @@ fn test_guard_with_unwind_safety() {
|
||||
|
||||
assert!(res.is_err());
|
||||
|
||||
assert_erased_state(&erased, 0, 0, Layout::new::<DropTracker>());
|
||||
assert_erased_state(&erased, 0, Layout::new::<DropTracker>());
|
||||
assert_eq!(drop_count.get(), 2);
|
||||
}
|
||||
|
||||
@@ -192,7 +180,7 @@ fn test_conversion_methods() {
|
||||
let initial_cap = vec.capacity();
|
||||
|
||||
let erased = TypeErasedVec::new(vec);
|
||||
assert_erased_state(&erased, 1, initial_cap, Layout::new::<String>());
|
||||
assert_erased_state(&erased, 1, Layout::new::<String>());
|
||||
|
||||
let Ok(restored) = erased.try_into_vec::<String>() else {
|
||||
panic!("try_into_vec failed despite matching layouts");
|
||||
@@ -207,6 +195,7 @@ fn test_conversion_methods() {
|
||||
let new_cap = guard.capacity();
|
||||
|
||||
let restored2 = guard.take();
|
||||
// Vec::from_raw_parts guarantees the exact capacity reconstructed matches the integer passed in.
|
||||
assert_eq!(restored2.capacity(), new_cap);
|
||||
}
|
||||
|
||||
@@ -221,7 +210,8 @@ fn test_try_into_vec_failure_and_recovery() {
|
||||
panic!("try_into_vec succeeded with mismatched layout");
|
||||
};
|
||||
|
||||
assert_erased_state(&recovered_erased, 0, initial_cap, Layout::new::<u32>());
|
||||
assert_erased_state(&recovered_erased, 0, Layout::new::<u32>());
|
||||
assert!(recovered_erased.capacity() >= initial_cap);
|
||||
|
||||
let Ok(restored) = recovered_erased.try_into_vec::<u32>() else {
|
||||
panic!("try_into_vec failed after recovery");
|
||||
@@ -273,15 +263,17 @@ fn test_sendable_clears_and_unpacks() {
|
||||
let drop_count = Rc::new(Cell::new(0));
|
||||
let mut vec = Vec::with_capacity(5);
|
||||
vec.push(DropTracker::new(drop_count.clone()));
|
||||
let initial_cap = vec.capacity();
|
||||
|
||||
let erased = TypeErasedVec::new(vec);
|
||||
assert_erased_state(&erased, 1, 5, Layout::new::<DropTracker>());
|
||||
assert_erased_state(&erased, 1, Layout::new::<DropTracker>());
|
||||
|
||||
let sendable = erased.send();
|
||||
assert_eq!(drop_count.get(), 1);
|
||||
|
||||
let unpacked = sendable.unpack();
|
||||
assert_erased_state(&unpacked, 0, 5, Layout::new::<DropTracker>());
|
||||
assert_erased_state(&unpacked, 0, Layout::new::<DropTracker>());
|
||||
assert!(unpacked.capacity() >= initial_cap);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -292,7 +284,7 @@ fn test_drop_cleans_up_allocation() {
|
||||
|
||||
{
|
||||
let erased = TypeErasedVec::new(vec);
|
||||
assert_erased_state(&erased, 1, 5, Layout::new::<DropTracker>());
|
||||
assert_erased_state(&erased, 1, Layout::new::<DropTracker>());
|
||||
assert_eq!(drop_count.get(), 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use crate::VecParts;
|
||||
use std::ptr::NonNull;
|
||||
|
||||
pub(crate) struct TypeErasedVecVtable {
|
||||
/// Function pointer to reserve additional capacity in the underlying Vec
|
||||
|
||||
Reference in New Issue
Block a user