#![feature(ptr_metadata, layout_for_ptr, unsize)]
#![no_std]
use core::alloc::Layout;
use core::marker::{PhantomData, Sized, Unsize};
use core::mem::{self, MaybeUninit};
use core::ops::{Deref, DerefMut};
use core::ptr::{self, NonNull, Pointee};
extern crate alloc;
use alloc::alloc::{alloc, dealloc, handle_alloc_error};
use alloc::boxed::Box;
#[cfg(test)]
mod tests;
mod r#impl;
pub struct Miny<T: ?Sized> {
meta: <T as Pointee>::Metadata,
data: MaybeUninit<*mut ()>,
marker: PhantomData<T>,
}
const fn goes_on_stack(layout: Layout) -> bool {
layout.size() <= mem::size_of::<*mut ()>() && layout.align() <= mem::align_of::<*mut ()>()
}
impl<T> Miny<T> {
pub fn new(value: T) -> Self {
Self {
meta: (),
data: if goes_on_stack(Layout::new::<T>()) {
let mut data = MaybeUninit::<*mut ()>::uninit();
unsafe { ptr::write(data.as_mut_ptr().cast::<T>(), value) };
data
} else {
MaybeUninit::new(Box::into_raw(Box::new(value)).cast::<()>())
},
marker: PhantomData,
}
}
#[inline]
#[must_use]
pub fn unsize<S: ?Sized>(this: Self) -> Miny<S>
where
T: Unsize<S>,
{
let meta = ptr::metadata(ptr::from_ref::<S>(&*this));
let data = this.data;
mem::forget(this);
Miny {
meta,
data,
marker: PhantomData,
}
}
#[inline]
#[must_use]
pub fn new_unsized<S: ?Sized>(value: T) -> Miny<S>
where
T: Unsize<S>,
{
Self::unsize(Self::new(value))
}
#[must_use]
pub fn into_inner(this: Self) -> T {
if goes_on_stack(Layout::new::<T>()) {
let data = this.data;
mem::forget(this);
unsafe { ptr::read(data.as_ptr().cast()) }
} else {
*unsafe { Self::heap_into_box(this) }
}
}
}
impl<T: ?Sized> Miny<T> {
#[inline]
unsafe fn heap_into_box(mut this: Self) -> Box<T> {
let res = unsafe { Box::from_raw(ptr::from_mut(this.as_mut())) };
mem::forget(this);
res
}
#[inline]
pub fn layout(this: &Self) -> Layout {
unsafe { Layout::for_value_raw(ptr::from_raw_parts::<T>(ptr::null::<()>(), this.meta)) }
}
#[inline]
pub fn on_stack(this: &Self) -> bool { goes_on_stack(Self::layout(this)) }
pub fn into_box(this: Self) -> Box<T> {
if Self::on_stack(&this) {
let layout = Self::layout(&this);
let data = if layout.size() == 0 {
NonNull::dangling()
} else {
NonNull::new(unsafe { alloc(layout) }).unwrap_or_else(|| handle_alloc_error(layout))
};
let src = this.data.as_ptr().cast::<u8>();
unsafe { ptr::copy_nonoverlapping(src, data.as_ptr(), layout.size()) };
let meta = this.meta;
mem::forget(this);
unsafe { Box::from_raw(ptr::from_raw_parts_mut(data.as_ptr(), meta)) }
} else {
unsafe { Self::heap_into_box(this) }
}
}
}
impl<T: ?Sized> Deref for Miny<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
let data = if Self::on_stack(self) {
self.data.as_ptr().cast::<()>()
} else {
unsafe { self.data.assume_init() }
};
unsafe { &*ptr::from_raw_parts(data, self.meta) }
}
}
impl<T: ?Sized> DerefMut for Miny<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
let data = if Self::on_stack(self) {
self.data.as_mut_ptr().cast::<()>()
} else {
unsafe { self.data.assume_init() }
};
unsafe { &mut *ptr::from_raw_parts_mut(data, self.meta) }
}
}
impl<T: ?Sized> From<Box<T>> for Miny<T> {
fn from(value: Box<T>) -> Self {
let layout = Layout::for_value(&*value);
let (val, meta) = Box::into_raw(value).to_raw_parts();
if goes_on_stack(layout) {
let mut data = MaybeUninit::<*mut ()>::uninit();
if layout.size() != 0 {
let dst = data.as_mut_ptr().cast::<u8>();
unsafe { ptr::copy_nonoverlapping(val.cast::<u8>(), dst, layout.size()) };
unsafe { dealloc(val.cast::<u8>(), layout) };
}
Self {
meta,
data,
marker: PhantomData,
}
} else {
Self {
meta,
data: MaybeUninit::new(val),
marker: PhantomData,
}
}
}
}
impl<T: ?Sized> Drop for Miny<T> {
fn drop(&mut self) {
if Self::on_stack(self) {
unsafe { ptr::drop_in_place(self.as_mut()) };
} else {
drop(unsafe { Box::from_raw(self.as_mut()) });
}
}
}