continue refactor
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
use crate::TypeErasedVec;
|
||||
use std::marker::PhantomData;
|
||||
use std::mem::ManuallyDrop;
|
||||
|
||||
/// Provides access to a `TypeErasedVec` with a temporarily fixed type `T`
|
||||
pub struct ContentGuard<'vec, T> {
|
||||
erased: &'vec mut TypeErasedVec,
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<'vec, T> ContentGuard<'vec, T> {
|
||||
pub(crate) fn new(erased: &'vec mut TypeErasedVec) -> Self {
|
||||
Self {
|
||||
erased,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn take(&mut self) -> Vec<T> {
|
||||
let erased = std::mem::replace(self.erased, TypeErasedVec::new(Vec::<T>::new()));
|
||||
let erased = ManuallyDrop::new(erased);
|
||||
// SAFETY: The erased pointer, length, and capacity are valid for a Vec<T>.
|
||||
// ManuallyDrop prevents the old TypeErasedVec from double-freeing the allocation.
|
||||
unsafe {
|
||||
Vec::from_raw_parts(
|
||||
erased.parts.ptr.as_ptr().cast::<T>(),
|
||||
erased.parts.len,
|
||||
erased.parts.cap,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with<R>(&mut self, f: impl FnOnce(&mut Vec<T>) -> R) -> R {
|
||||
let mut vec = self.take();
|
||||
// This is unwind-safe. If the closure panics, the inner `Vec<T>` drops normally.
|
||||
// The `erased` reference was swapped with a 0-capacity vector inside `take()`,
|
||||
// preventing any double-free or memory leak.
|
||||
let res = f(&mut vec);
|
||||
*self.erased = TypeErasedVec::new(vec);
|
||||
res
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.with(|vec| vec.clear());
|
||||
}
|
||||
|
||||
pub fn reserve(&mut self, additional: usize) {
|
||||
self.with(|vec| vec.reserve(additional));
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn capacity(&self) -> usize {
|
||||
self.erased.capacity()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn length(&self) -> usize {
|
||||
self.erased.length()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.erased.is_empty()
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
use crate::TypeErasedVec;
|
||||
|
||||
pub struct SendableTypeErasedVec(TypeErasedVec);
|
||||
|
||||
impl SendableTypeErasedVec {
|
||||
pub(crate) fn new(vec: TypeErasedVec) -> Self {
|
||||
Self(vec)
|
||||
}
|
||||
|
||||
pub fn unpack(self) -> TypeErasedVec {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: We ensure this type never contains any elements and only allow converting back to an empty TypeErasedVec
|
||||
unsafe impl Send for SendableTypeErasedVec {}
|
||||
|
||||
// SAFETY: We ensure this type never contains any elements and only allow converting back to an empty TypeErasedVec
|
||||
unsafe impl Sync for SendableTypeErasedVec {}
|
||||
@@ -0,0 +1,66 @@
|
||||
use crate::VecParts;
|
||||
use std::ptr::NonNull;
|
||||
|
||||
pub(crate) struct TypeErasedVecVtable {
|
||||
/// Function pointer to reserve additional capacity in the underlying Vec
|
||||
pub reserve_vector: unsafe fn(VecParts, usize) -> VecParts,
|
||||
/// Function pointer to clear the underlying capacity of its old elements
|
||||
pub clear_vector: unsafe fn(VecParts) -> VecParts,
|
||||
/// Function pointer to the original type's drop logic.
|
||||
pub drop_vector: unsafe fn(VecParts),
|
||||
}
|
||||
|
||||
impl TypeErasedVecVtable {
|
||||
pub fn new<T>() -> Self {
|
||||
unsafe fn drop_vec<T>(parts: VecParts) {
|
||||
// SAFETY: We reconstruct the Vec to let its Drop impl handle deallocation.
|
||||
_ = unsafe {
|
||||
Vec::from_raw_parts(parts.ptr.as_ptr().cast::<T>(), parts.len, parts.cap)
|
||||
};
|
||||
}
|
||||
|
||||
unsafe fn clear_vec<T>(parts: VecParts) -> VecParts {
|
||||
// SAFETY: We reconstruct the Vec to call clear, dropping only the elements.
|
||||
// Using into_raw_parts ensures we don't accidentally drop the allocation itself.
|
||||
unsafe {
|
||||
let mut vec =
|
||||
Vec::from_raw_parts(parts.ptr.as_ptr().cast::<T>(), parts.len, parts.cap);
|
||||
vec.clear();
|
||||
let (new_ptr, new_len, new_cap) = Vec::into_raw_parts(vec);
|
||||
debug_assert!(std::ptr::eq(new_ptr, parts.ptr.as_ptr().cast()));
|
||||
debug_assert_eq!(new_cap, parts.cap);
|
||||
debug_assert_eq!(new_len, 0);
|
||||
|
||||
VecParts {
|
||||
ptr: NonNull::new_unchecked(new_ptr).cast(),
|
||||
len: new_len,
|
||||
cap: new_cap,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn reserve_vec<T>(parts: VecParts, additional: usize) -> VecParts {
|
||||
// SAFETY: We reconstruct the Vec to trigger a reserve.
|
||||
// Capacity might change upon reallocation.
|
||||
unsafe {
|
||||
let mut vec =
|
||||
Vec::from_raw_parts(parts.ptr.as_ptr().cast::<T>(), parts.len, parts.cap);
|
||||
vec.reserve(additional);
|
||||
let (new_ptr, new_len, new_cap) = Vec::into_raw_parts(vec);
|
||||
debug_assert_eq!(new_len, parts.len);
|
||||
|
||||
VecParts {
|
||||
ptr: NonNull::new_unchecked(new_ptr).cast(),
|
||||
len: new_len,
|
||||
cap: new_cap,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Self {
|
||||
reserve_vector: reserve_vec::<T>,
|
||||
clear_vector: clear_vec::<T>,
|
||||
drop_vector: drop_vec::<T>,
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user