#[cfg(doc)]
use std::boxed::Box;
use crate::VClone;
use super::{Initializer, Layout, VarLen};
use alloc::alloc;
use core::pin::Pin;
use core::ptr::NonNull;
pub struct VBox<T: VarLen>(NonNull<T>);
#[inline(never)]
#[cold]
fn allocation_overflow() -> ! {
panic!("Allocation size overflow")
}
#[allow(rustdoc::missing_doc_code_examples)]
impl<T: VarLen> VBox<T> {
pub fn new(init: impl Initializer<T>) -> Self {
let layout = init
.calculate_layout_cautious()
.unwrap_or_else(|| allocation_overflow());
let alloc_layout = alloc::Layout::from_size_align(layout.size(), T::ALIGN)
.unwrap_or_else(|_| allocation_overflow());
unsafe {
let p = alloc::alloc(alloc_layout) as *mut T;
let layout_size = layout.size();
init.initialize(NonNull::new_unchecked(p), layout);
let mut p = NonNull::new_unchecked(p);
debug_assert_eq!(p.as_mut().calculate_layout().size(), layout_size);
VBox(p)
}
}
pub fn as_mut(&mut self) -> Pin<&mut T> {
unsafe { Pin::new_unchecked(self.0.as_mut()) }
}
pub unsafe fn into_raw(self) -> *mut T {
let result = self.0.as_ptr();
core::mem::forget(self);
result
}
pub unsafe fn from_raw(raw: *mut T) -> Self {
VBox(NonNull::new_unchecked(raw))
}
}
impl<T: VarLen> Drop for VBox<T> {
fn drop(&mut self) {
unsafe {
let layout = T::calculate_layout(&*self);
let alloc_layout = alloc::Layout::from_size_align_unchecked(layout.size(), T::ALIGN);
T::vdrop(self.as_mut(), layout);
alloc::dealloc(self.0.as_ptr() as *mut u8, alloc_layout);
}
}
}
impl<T: VarLen> core::ops::Deref for VBox<T> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
unsafe { self.0.as_ref() }
}
}
unsafe impl<T: VarLen> Initializer<T> for VBox<T> {
unsafe fn initialize(self, dst: NonNull<T>, layout: T::Layout) {
let VBox(ptr) = self;
let ptr = ptr.as_ptr().cast::<u8>();
let size = layout.size();
let layout = alloc::Layout::from_size_align_unchecked(size, T::ALIGN);
core::ptr::copy_nonoverlapping(ptr, dst.as_ptr().cast::<u8>(), size);
alloc::dealloc(ptr, layout);
core::mem::forget(self);
}
#[inline]
fn calculate_layout_cautious(&self) -> Option<T::Layout> {
Some(T::calculate_layout(&*self))
}
}
impl<T: for<'a> VClone<'a>> Clone for VBox<T> {
fn clone(&self) -> Self {
VBox::new(self.vclone())
}
}
#[cfg(test)]
mod tests {
use alloc::string::{String, ToString};
#[test]
fn vdrop() {
use crate::prelude::*;
type Ty = Tup2<FixedLen<String>, Str>;
let v: VBox<Ty> = VBox::new(tup2::Init(
FixedLen("hello".to_string()),
Str::copy("world"),
));
drop(v);
}
}