#![no_std]
extern crate alloc;
use {
crate::polyfill::*,
alloc::{
alloc::{alloc, dealloc, handle_alloc_error, Layout, LayoutErr},
boxed::Box,
rc::Rc,
sync::Arc,
},
core::{
cmp::{self, PartialEq},
fmt::{self, Debug},
hash,
marker::PhantomData,
mem::ManuallyDrop,
ops::{Deref, DerefMut},
ptr::{self, NonNull},
},
};
mod polyfill;
pub type ErasedPtr = NonNull<priv_in_pub::Erased>;
#[doc(hidden)]
pub mod priv_in_pub {
pub struct Erased {
#[allow(unused)]
raw: u8,
}
}
#[repr(C)]
#[derive(Debug, Eq, PartialEq, Hash)]
pub struct ThinData<Head, SliceItem> {
len: usize,
pub head: Head,
pub slice: [SliceItem],
}
impl<Head, SliceItem> ThinData<Head, SliceItem> {
fn len(ptr: ErasedPtr) -> NonNull<usize> {
ptr.cast()
}
fn erase(ptr: NonNull<Self>) -> ErasedPtr {
ptr.cast()
}
unsafe fn fatten_const(ptr: ErasedPtr) -> NonNull<Self> {
let len = ptr::read(Self::len(ptr).as_ptr());
let slice = make_slice(ptr.cast::<SliceItem>().as_ptr(), len);
NonNull::new_unchecked(slice as *const Self as *mut Self)
}
unsafe fn fatten_mut(ptr: ErasedPtr) -> NonNull<Self> {
let len = ptr::read(Self::len(ptr).as_ptr());
let slice = make_slice_mut(ptr.cast::<SliceItem>().as_ptr(), len);
NonNull::new_unchecked(slice as *mut Self)
}
}
impl<SliceItem: PartialEq> PartialEq<[SliceItem]> for ThinData<(), SliceItem> {
fn eq(&self, other: &[SliceItem]) -> bool {
&self.slice == other
}
}
macro_rules! thin_holder {
( #[nodrop] for $thin:ident<$($a:lifetime,)* Head, SliceItem> as $fat:ident<$($b:lifetime,)* ThinData<Head, SliceItem>> with $fatten:ident ) => {
impl<$($a,)* Head, SliceItem> $thin<$($a,)* Head, SliceItem> {
pub unsafe fn from_erased(ptr: ErasedPtr) -> Self {
Self {
raw: ptr,
marker: PhantomData,
}
}
pub fn erase(this: Self) -> ErasedPtr {
let this = ManuallyDrop::new(this);
this.raw
}
}
impl<$($a,)* Head, SliceItem> From<$fat<$($b,)* ThinData<Head, SliceItem>>> for $thin<$($a,)* Head, SliceItem> {
fn from(this: $fat<$($b,)* ThinData<Head, SliceItem>>) -> $thin<$($a,)* Head, SliceItem> {
unsafe {
let this = NonNull::new_unchecked($fat::into_raw(this) as *mut _);
Self::from_erased(ThinData::<Head, SliceItem>::erase(this))
}
}
}
impl<$($a,)* Head, SliceItem> Deref for $thin<$($a,)* Head, SliceItem>
where
$fat<$($b,)* ThinData<Head, SliceItem>>: Deref,
{
type Target = ThinData<Head, SliceItem>;
fn deref(&self) -> &ThinData<Head, SliceItem> {
unsafe { &*ThinData::fatten_const(self.raw).as_ptr() }
}
}
impl<$($a,)* Head, SliceItem> DerefMut for $thin<$($a,)* Head, SliceItem>
where
$fat<$($b,)* ThinData<Head, SliceItem>>: DerefMut,
{
fn deref_mut(&mut self) -> &mut ThinData<Head, SliceItem> {
unsafe { &mut *ThinData::fatten_mut(self.raw).as_ptr() }
}
}
impl<$($a,)* Head, SliceItem> Debug for $thin<$($a,)* Head, SliceItem>
where
$fat<$($b,)* ThinData<Head, SliceItem>>: Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
unsafe {
let this = ManuallyDrop::new($fat::from_raw(ThinData::fatten_const(self.raw).as_ptr()));
this.fmt(f)
}
}
}
unsafe impl<$($a,)* Head, SliceItem> Send for $thin<$($a,)* Head, SliceItem> where
$fat<$($b,)* ThinData<Head, SliceItem>>: Send
{
}
unsafe impl<$($a,)* Head, SliceItem> Sync for $thin<$($a,)* Head, SliceItem> where
$fat<$($b,)* ThinData<Head, SliceItem>>: Sync
{
}
impl<$($a,)* Head, SliceItem> cmp::Eq for $thin<$($a,)* Head, SliceItem> where
$fat<$($b,)* ThinData<Head, SliceItem>>: cmp::Eq,
{
}
impl<$($a,)* Head, SliceItem> PartialEq for $thin<$($a,)* Head, SliceItem>
where
$fat<$($b,)* ThinData<Head, SliceItem>>: PartialEq,
{
fn eq(&self, other: &Self) -> bool {
unsafe {
let other = ManuallyDrop::new($fat::from_raw(ThinData::fatten_const(other.raw).as_ptr()));
<Self as PartialEq<$fat<$($b,)* ThinData<Head, SliceItem>>>>::eq(self, &other)
}
}
}
impl<$($a,)* Head, SliceItem> PartialEq<$fat<$($b,)* ThinData<Head, SliceItem>>> for $thin<$($a,)* Head, SliceItem>
where
$fat<$($b,)* ThinData<Head, SliceItem>>: PartialEq,
{
fn eq(&self, other: &$fat<$($b,)* ThinData<Head, SliceItem>>) -> bool {
unsafe {
let this = ManuallyDrop::new($fat::from_raw(ThinData::fatten_const(self.raw).as_ptr()));
<$fat<$($b,)* ThinData<Head, SliceItem>> as PartialEq>::eq(&this, other)
}
}
}
impl<$($a,)* Head, SliceItem> hash::Hash for $thin<$($a,)* Head, SliceItem>
where
$fat<$($b,)* ThinData<Head, SliceItem>>: hash::Hash,
{
fn hash<H>(&self, state: &mut H)
where
H: hash::Hasher,
{
unsafe {
let this = ManuallyDrop::new($fat::from_raw(ThinData::fatten_const(self.raw).as_ptr()));
<$fat<$($b,)* ThinData<Head, SliceItem>> as hash::Hash>::hash(&this, state)
}
}
}
};
( for $thin:ident<$($a:lifetime,)* Head, SliceItem> as $fat:ident<$($b:lifetime,)* ThinData<Head, SliceItem>> with $fatten:ident ) => {
impl<$($a,)* Head, SliceItem> Drop for $thin<$($a,)* Head, SliceItem> {
fn drop(&mut self) {
let this = unsafe { $fat::from_raw(ThinData::$fatten(self.raw).as_ptr()) };
drop::<$fat<$($b,)* ThinData<Head, SliceItem>>>(this)
}
}
thin_holder!(#[nodrop] for $thin<$($a,)* Head, SliceItem> as $fat<$($b,)* ThinData<Head, SliceItem>> with $fatten );
};
}
pub struct ThinBox<Head, SliceItem> {
raw: ErasedPtr,
marker: PhantomData<Box<ThinData<Head, SliceItem>>>,
}
thin_holder!(for ThinBox<Head, SliceItem> as Box<ThinData<Head, SliceItem>> with fatten_mut);
impl<Head, SliceItem> ThinBox<Head, SliceItem> {
fn layout(len: usize) -> Result<(Layout, [usize; 3]), LayoutErr> {
let length_layout = Layout::new::<usize>();
let head_layout = Layout::new::<Head>();
let slice_layout = layout_array::<SliceItem>(len)?;
repr_c_3([length_layout, head_layout, slice_layout])
}
unsafe fn alloc(len: usize, layout: Layout) -> NonNull<ThinData<Head, SliceItem>> {
let ptr: ErasedPtr = NonNull::new(alloc(layout))
.unwrap_or_else(|| handle_alloc_error(layout))
.cast();
ptr::write(ThinData::<Head, SliceItem>::len(ptr).as_ptr(), len);
ThinData::fatten_mut(ptr.cast())
}
pub fn new<I>(head: Head, slice: I) -> Self
where
I: IntoIterator<Item = SliceItem>,
I::IntoIter: ExactSizeIterator,
{
struct InProgress<Head, SliceItem> {
raw: NonNull<ThinData<Head, SliceItem>>,
written_len: usize,
layout: Layout,
head_offset: usize,
slice_offset: usize,
}
impl<Head, SliceItem> Drop for InProgress<Head, SliceItem> {
fn drop(&mut self) {
let raw_ptr = ThinData::erase(self.raw).as_ptr();
unsafe {
let slice = make_slice_mut(
raw_ptr.add(self.slice_offset).cast::<SliceItem>(),
self.written_len,
);
ptr::drop_in_place(slice);
dealloc(raw_ptr.cast(), self.layout);
}
}
}
impl<Head, SliceItem> InProgress<Head, SliceItem> {
fn raw_ptr(&self) -> ErasedPtr {
ThinData::erase(self.raw)
}
fn new(len: usize) -> Self {
let (layout, [_, head_offset, slice_offset]) =
ThinBox::<Head, SliceItem>::layout(len)
.unwrap_or_else(|e| panic!("oversize box: {}", e));
InProgress {
raw: unsafe { ThinBox::alloc(len, layout) },
written_len: 0,
layout,
head_offset,
slice_offset,
}
}
unsafe fn push(&mut self, item: SliceItem) {
self.raw_ptr()
.as_ptr()
.add(self.slice_offset)
.cast::<SliceItem>()
.add(self.written_len)
.write(item);
self.written_len += 1;
}
unsafe fn finish(self, head: Head) -> ThinBox<Head, SliceItem> {
let this = ManuallyDrop::new(self);
let ptr = this.raw_ptr();
ptr::write(ptr.as_ptr().add(this.head_offset).cast(), head);
let out = ThinBox::from_erased(ptr);
assert_eq!(this.layout, Layout::for_value(&*out));
out
}
}
let mut items = slice.into_iter();
let len = items.len();
unsafe {
let mut this = InProgress::new(len);
for _ in 0..len {
let slice_item = items
.next()
.expect("ExactSizeIterator over-reported length");
this.push(slice_item);
}
assert!(
items.next().is_none(),
"ExactSizeIterator under-reported length"
);
this.finish(head)
}
}
}
impl<Head, SliceItem> From<ThinBox<Head, SliceItem>> for Box<ThinData<Head, SliceItem>> {
fn from(this: ThinBox<Head, SliceItem>) -> Self {
unsafe {
let this = ManuallyDrop::new(this);
Box::from_raw(ThinData::fatten_mut(this.raw).as_ptr())
}
}
}
impl<Head, SliceItem> Clone for ThinBox<Head, SliceItem>
where
Head: Clone,
SliceItem: Clone,
{
fn clone(&self) -> Self {
ThinBox::new(self.head.clone(), self.slice.iter().cloned())
}
}
pub struct ThinArc<Head, SliceItem> {
raw: ErasedPtr,
marker: PhantomData<Arc<ThinData<Head, SliceItem>>>,
}
thin_holder!(for ThinArc<Head, SliceItem> as Arc<ThinData<Head, SliceItem>> with fatten_const);
impl<Head, SliceItem> ThinArc<Head, SliceItem> {
pub fn new<I>(head: Head, slice: I) -> Self
where
I: IntoIterator<Item = SliceItem>,
I::IntoIter: ExactSizeIterator,
{
let boxed: Box<ThinData<Head, SliceItem>> = ThinBox::new(head, slice).into();
let arc: Arc<ThinData<Head, SliceItem>> = boxed.into();
arc.into()
}
}
impl<Head, SliceItem> From<ThinArc<Head, SliceItem>> for Arc<ThinData<Head, SliceItem>> {
fn from(this: ThinArc<Head, SliceItem>) -> Self {
unsafe {
let this = ManuallyDrop::new(this);
Arc::from_raw(ThinData::fatten_const(this.raw).as_ptr())
}
}
}
impl<Head, SliceItem> Clone for ThinArc<Head, SliceItem>
where
Arc<ThinData<Head, SliceItem>>: Clone,
{
fn clone(&self) -> Self {
unsafe {
let this = ManuallyDrop::new(Arc::from_raw(ThinData::fatten_const(self.raw).as_ptr()));
ManuallyDrop::into_inner(ManuallyDrop::clone(&this)).into()
}
}
}
pub struct ThinRc<Head, SliceItem> {
raw: ErasedPtr,
marker: PhantomData<Rc<ThinData<Head, SliceItem>>>,
}
thin_holder!(for ThinRc<Head, SliceItem> as Rc<ThinData<Head, SliceItem>> with fatten_const);
impl<Head, SliceItem> ThinRc<Head, SliceItem> {
pub fn new<I>(head: Head, slice: I) -> Self
where
I: IntoIterator<Item = SliceItem>,
I::IntoIter: ExactSizeIterator,
{
let boxed: Box<ThinData<Head, SliceItem>> = ThinBox::new(head, slice).into();
let arc: Rc<ThinData<Head, SliceItem>> = boxed.into();
arc.into()
}
}
impl<Head, SliceItem> From<ThinRc<Head, SliceItem>> for Rc<ThinData<Head, SliceItem>> {
fn from(this: ThinRc<Head, SliceItem>) -> Self {
unsafe {
let this = ManuallyDrop::new(this);
Rc::from_raw(ThinData::fatten_const(this.raw).as_ptr())
}
}
}
impl<Head, SliceItem> Clone for ThinRc<Head, SliceItem>
where
Rc<ThinData<Head, SliceItem>>: Clone,
{
fn clone(&self) -> Self {
unsafe {
let this = ManuallyDrop::new(Rc::from_raw(ThinData::fatten_const(self.raw).as_ptr()));
ManuallyDrop::into_inner(ManuallyDrop::clone(&this)).into()
}
}
}
pub struct ThinRef<'a, Head, SliceItem> {
raw: ErasedPtr,
marker: PhantomData<&'a ThinData<Head, SliceItem>>,
}
thin_holder!(#[nodrop] for ThinRef<'a, Head, SliceItem> as Ref<'a, ThinData<Head, SliceItem>> with fatten_const);
impl<'a, Head, SliceItem> Copy for ThinRef<'a, Head, SliceItem> where
&'a ThinData<Head, SliceItem>: Copy
{
}
impl<'a, Head, SliceItem> Clone for ThinRef<'a, Head, SliceItem>
where
&'a ThinData<Head, SliceItem>: Clone,
{
fn clone(&self) -> Self {
*self
}
}
impl<'a, Head, SliceItem> From<ThinRef<'a, Head, SliceItem>> for &'a ThinData<Head, SliceItem> {
fn from(this: ThinRef<'a, Head, SliceItem>) -> Self {
unsafe { Ref::from_raw(ThinData::fatten_const(this.raw).as_ptr()) }
}
}
pub struct ThinRefMut<'a, Head, SliceItem> {
raw: ErasedPtr,
marker: PhantomData<&'a mut ThinData<Head, SliceItem>>,
}
thin_holder!(#[nodrop] for ThinRefMut<'a, Head, SliceItem> as Ref<'a, ThinData<Head, SliceItem>> with fatten_const);
impl<'a, Head, SliceItem> From<ThinRefMut<'a, Head, SliceItem>>
for &'a mut ThinData<Head, SliceItem>
{
fn from(this: ThinRefMut<'a, Head, SliceItem>) -> Self {
unsafe { RefMut::from_raw(ThinData::fatten_mut(this.raw).as_ptr()) }
}
}
pub struct ThinPtr<Head, SliceItem> {
raw: ErasedPtr,
marker: PhantomData<NonNull<ThinData<Head, SliceItem>>>,
}
thin_holder!(#[nodrop] for ThinPtr<Head, SliceItem> as NonNull<ThinData<Head, SliceItem>> with fatten_mut);
impl<Head, SliceItem> Copy for ThinPtr<Head, SliceItem> where
NonNull<ThinData<Head, SliceItem>>: Copy
{
}
impl<Head, SliceItem> Clone for ThinPtr<Head, SliceItem>
where
NonNull<ThinData<Head, SliceItem>>: Clone,
{
fn clone(&self) -> Self {
*self
}
}
impl<Head, SliceItem> From<ThinPtr<Head, SliceItem>> for NonNull<ThinData<Head, SliceItem>> {
fn from(this: ThinPtr<Head, SliceItem>) -> Self {
unsafe { ThinData::fatten_mut(this.raw) }
}
}
#[allow(
missing_docs,
clippy::missing_safety_doc,
clippy::should_implement_trait
)]
impl<Head, SliceItem> ThinPtr<Head, SliceItem> {
pub unsafe fn as_ptr(self) -> *mut ThinData<Head, SliceItem> {
let nn: NonNull<_> = self.into();
nn.as_ptr()
}
pub unsafe fn as_ref(&self) -> &ThinData<Head, SliceItem> {
&*self.as_ptr()
}
pub unsafe fn as_mut(&mut self) -> &mut ThinData<Head, SliceItem> {
&mut *self.as_ptr()
}
}
unsafe trait RawExt<T: ?Sized> {
unsafe fn from_raw(ptr: *const T) -> Self;
unsafe fn into_raw(self) -> *const T;
}
unsafe trait RawMutExt<T: ?Sized> {
unsafe fn from_raw(ptr: *mut T) -> Self;
unsafe fn into_raw(self) -> *mut T;
}
type Ref<'a, T> = &'a T;
unsafe impl<'a, T: ?Sized> RawExt<T> for Ref<'a, T> {
unsafe fn from_raw(ptr: *const T) -> Self {
&*ptr
}
unsafe fn into_raw(self) -> *const T {
self
}
}
type RefMut<'a, T> = &'a mut T;
unsafe impl<'a, T: ?Sized> RawMutExt<T> for RefMut<'a, T> {
unsafe fn from_raw(ptr: *mut T) -> Self {
&mut *ptr
}
unsafe fn into_raw(self) -> *mut T {
self
}
}
unsafe impl<T: ?Sized> RawMutExt<T> for NonNull<T> {
unsafe fn from_raw(ptr: *mut T) -> Self {
NonNull::new_unchecked(ptr)
}
unsafe fn into_raw(self) -> *mut T {
NonNull::as_ptr(self)
}
}