#![no_std]
#[cfg(feature = "alloc")]
extern crate alloc;
#[cfg(test)]
mod tests;
#[cfg(feature = "alloc")]
use alloc::{
alloc::{alloc, dealloc, handle_alloc_error},
boxed::Box,
rc::Rc,
sync::Arc,
};
use core::{
alloc::{Layout, LayoutError},
borrow::Borrow,
convert::Infallible,
mem::{self, MaybeUninit},
ptr::{self, NonNull},
};
#[cfg(feature = "simple-dst-derive")]
pub use simple_dst_derive::{CloneToUninit, Dst, ToOwned};
pub unsafe trait Dst {
fn len(&self) -> usize;
fn is_empty(&self) -> bool {
self.len() == 0
}
fn layout(len: usize) -> Result<Layout, LayoutError>;
fn retype(ptr: NonNull<u8>, len: usize) -> NonNull<Self>;
}
unsafe impl<T> Dst for [T] {
fn len(&self) -> usize {
self.len()
}
fn layout(len: usize) -> Result<Layout, LayoutError> {
Layout::array::<T>(len)
}
fn retype(ptr: NonNull<u8>, len: usize) -> NonNull<Self> {
NonNull::slice_from_raw_parts(ptr.cast(), len)
}
}
unsafe impl Dst for str {
fn len(&self) -> usize {
self.len()
}
fn layout(len: usize) -> Result<Layout, LayoutError> {
Layout::array::<u8>(len)
}
fn retype(ptr: NonNull<u8>, len: usize) -> NonNull<Self> {
unsafe {
NonNull::new_unchecked(ptr::slice_from_raw_parts_mut(ptr.as_ptr(), len) as *mut Self)
}
}
}
pub unsafe trait CloneToUninit {
unsafe fn clone_to_uninit(&self, dest: *mut u8);
}
unsafe impl<T: Clone> CloneToUninit for T {
#[inline]
unsafe fn clone_to_uninit(&self, dest: *mut u8) {
unsafe {
dest.cast::<Self>().write(self.clone());
}
}
}
unsafe impl<T: Clone> CloneToUninit for [T] {
unsafe fn clone_to_uninit(&self, dest: *mut u8) {
struct InitializingSlice<'a, T> {
data: &'a mut [MaybeUninit<T>],
initialized_len: usize,
}
impl<'a, T> InitializingSlice<'a, T> {
#[inline]
fn from_fully_uninit(data: &'a mut [MaybeUninit<T>]) -> Self {
Self {
data,
initialized_len: 0,
}
}
#[inline]
fn push(&mut self, value: T) {
#[allow(
clippy::indexing_slicing,
reason = "the possibility of panicking is documented above"
)]
MaybeUninit::write(&mut self.data[self.initialized_len], value);
self.initialized_len += 1;
}
}
impl<T> Drop for InitializingSlice<'_, T> {
#[cold] fn drop(&mut self) {
#[allow(
clippy::indexing_slicing,
reason = "the indexing can't panic, as documented by the SAFETY comment"
)]
unsafe {
self.data[..self.initialized_len].assume_init_drop();
}
}
}
let len = self.len();
let dest = ptr::slice_from_raw_parts_mut(dest.cast::<T>(), len);
debug_assert_eq!(
len,
dest.len(),
"clone_to_uninit() source and destination must have equal lengths",
);
let uninit_ref = unsafe { &mut *(dest as *mut [MaybeUninit<T>]) };
let mut initializing = InitializingSlice::from_fully_uninit(uninit_ref);
for element_ref in self {
initializing.push(element_ref.clone());
}
mem::forget(initializing);
}
}
unsafe impl CloneToUninit for str {
#[inline]
unsafe fn clone_to_uninit(&self, dest: *mut u8) {
unsafe { self.as_bytes().clone_to_uninit(dest) }
}
}
unsafe impl CloneToUninit for core::ffi::CStr {
unsafe fn clone_to_uninit(&self, dest: *mut u8) {
unsafe { self.to_bytes_with_nul().clone_to_uninit(dest) }
}
}
pub unsafe trait AllocDst<T: ?Sized + Dst>: Sized + Borrow<T> {
unsafe fn try_new_dst<F, E>(len: usize, layout: Layout, init: F) -> Result<Self, E>
where
F: FnOnce(NonNull<T>) -> Result<(), E>;
unsafe fn new_dst<F>(len: usize, layout: Layout, init: F) -> Self
where
F: FnOnce(NonNull<T>),
{
let Ok(a) = unsafe {
Self::try_new_dst::<_, Infallible>(len, layout, |ptr| {
init(ptr);
Ok(())
})
};
a
}
}
#[cfg(feature = "alloc")]
unsafe impl<T: ?Sized + Dst> AllocDst<T> for Box<T> {
unsafe fn try_new_dst<F, E>(len: usize, layout: Layout, init: F) -> Result<Self, E>
where
F: FnOnce(NonNull<T>) -> Result<(), E>,
{
struct RawBox<T: ?Sized + Dst>(ptr::NonNull<T>, Layout);
impl<T: ?Sized + Dst> RawBox<T> {
fn new(len: usize, layout: Layout) -> Self {
let ptr = if layout.size() == 0 {
layout.dangling_ptr()
} else {
NonNull::new(unsafe { alloc(layout) })
.unwrap_or_else(|| handle_alloc_error(layout))
};
let ptr = T::retype(ptr, len);
Self(ptr, layout)
}
}
impl<T: ?Sized + Dst> Drop for RawBox<T> {
fn drop(&mut self) {
if self.1.size() != 0 {
unsafe {
dealloc(self.0.cast().as_ptr(), self.1);
};
}
}
}
let raw = RawBox::new(len, layout);
init(raw.0)?;
let b = unsafe { Box::from_raw(raw.0.as_ptr()) };
mem::forget(raw);
Ok(b)
}
}
#[cfg(feature = "alloc")]
unsafe impl<T: ?Sized + Dst> AllocDst<T> for Rc<T> {
unsafe fn try_new_dst<F, E>(len: usize, layout: Layout, init: F) -> Result<Self, E>
where
F: FnOnce(NonNull<T>) -> Result<(), E>,
{
Ok(Self::from(unsafe { Box::try_new_dst(len, layout, init) }?))
}
}
#[cfg(feature = "alloc")]
unsafe impl<T: ?Sized + Dst> AllocDst<T> for Arc<T> {
unsafe fn try_new_dst<F, E>(len: usize, layout: Layout, init: F) -> Result<Self, E>
where
F: FnOnce(NonNull<T>) -> Result<(), E>,
{
Ok(Self::from(unsafe { Box::try_new_dst(len, layout, init) }?))
}
}