use std::{
alloc::{self, Layout},
borrow::{Borrow, Cow},
fmt::{self, Debug, Formatter},
iter::FromIterator,
mem,
ops::Deref,
ptr::{self, NonNull},
slice,
};
pub const TYPE_ID: u32 = 0;
pub const ALIGN: usize = 16;
#[repr(transparent)]
pub struct AscBox<T>
where
T: AscBoxed + ?Sized,
{
data: NonNull<T::Target>,
}
pub unsafe trait AscBoxed {
type Target: Sized;
type Ref: Borrow<Self> + Sized;
}
unsafe impl<T> AscBoxed for T
where
T: Sized,
{
type Target = T;
type Ref = AscRef<T>;
}
unsafe impl<T> AscBoxed for [T]
where
T: Sized,
{
type Target = T;
type Ref = AscSlice<T>;
}
impl<T> AscBox<T> {
pub fn new(value: T) -> Self {
let data = unsafe {
let data = alloc_box::<T>(TYPE_ID, 1);
data.as_ptr().write(value);
data
};
Self { data }
}
}
impl<T> AscBox<[T]> {
pub fn from_slice(items: &[T]) -> Self
where
T: Copy,
{
let data = unsafe {
let data = alloc_box::<T>(TYPE_ID, items.len());
ptr::copy_nonoverlapping(items.as_ptr(), data.as_ptr(), items.len());
data
};
Self { data }
}
pub fn with_len(len: usize, items: impl IntoIterator<Item = T>) -> Self {
let mut items = items.into_iter();
let data = unsafe {
let data = alloc_box::<T>(TYPE_ID, len);
let drop_array_and_panic = |count| {
drop_box::<T>(data, count);
panic!("iterator does not match specified length");
};
for i in 0..len {
let item = items
.next()
.unwrap_or_else(|| drop_array_and_panic(i));
data.as_ptr().cast::<T>().add(i).write(item);
}
if items.next().is_some() {
drop_array_and_panic(len);
}
data
};
Self { data }
}
}
impl<T> AscBox<T>
where
T: AscBoxed + ?Sized,
{
pub fn as_asc_ref(&self) -> &T::Ref {
unsafe { &*self.data.as_ptr().cast() }
}
pub fn as_ptr(&self) -> *const T::Ref {
self.as_asc_ref() as _
}
pub fn owned(self) -> AscCow<'static, T>
where
T::Ref: ToOwned<Owned = Self>,
{
Cow::Owned(self)
}
}
impl<T> Borrow<AscRef<T>> for AscBox<T> {
fn borrow(&self) -> &AscRef<T> {
self.as_asc_ref()
}
}
impl<T> Borrow<AscSlice<T>> for AscBox<[T]> {
fn borrow(&self) -> &AscSlice<T> {
self.as_asc_ref()
}
}
impl<T> Clone for AscBox<T>
where
T: Clone,
{
fn clone(&self) -> Self {
self.as_asc_ref().to_owned()
}
}
impl<T> Clone for AscBox<[T]>
where
T: Clone,
{
fn clone(&self) -> Self {
self.as_asc_ref().to_owned()
}
}
impl<T> Debug for AscBox<T>
where
T: AscBoxed + Debug + ?Sized,
{
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.debug_tuple("AscBox")
.field(&self.as_asc_ref().borrow())
.finish()
}
}
impl<T> Drop for AscBox<T>
where
T: AscBoxed + ?Sized,
{
fn drop(&mut self) {
unsafe {
let len = {
let header = AscHeader::for_data(self.data.as_ptr());
header.len::<T::Target>()
};
drop_box(self.data, len);
}
}
}
impl<T> FromIterator<T> for AscBox<[T]> {
fn from_iter<I>(iter: I) -> Self
where
I: IntoIterator<Item = T>,
{
let iter = iter.into_iter();
let (len, max_len) = iter.size_hint();
if Some(len) != max_len {
panic!("cannot collect iterator with unknown size");
}
Self::with_len(len, iter)
}
}
#[repr(transparent)]
pub struct AscNullableBox<T>
where
T: AscBoxed + ?Sized,
{
data: *mut T::Target,
}
impl<T> AscNullableBox<T>
where
T: AscBoxed + ?Sized,
{
pub fn as_asc_ref(&self) -> Option<&T::Ref> {
if self.data.is_null() {
return None;
}
Some(unsafe { &*self.data.cast() })
}
}
impl<T> Drop for AscNullableBox<T>
where
T: AscBoxed + ?Sized,
{
fn drop(&mut self) {
if let Some(data) = NonNull::new(self.data) {
drop(AscBox::<T> { data })
}
}
}
pub type AscCow<'a, T> = Cow<'a, <T as AscBoxed>::Ref>;
#[repr(transparent)]
pub struct AscRef<T> {
inner: T,
}
impl<T> AscRef<T> {
pub fn borrowed(&self) -> AscCow<T>
where
T: Clone,
{
Cow::Borrowed(self)
}
pub fn as_ptr(&self) -> *const Self {
self as _
}
}
impl<T> Borrow<T> for AscRef<T> {
fn borrow(&self) -> &T {
&self.inner
}
}
impl<T> Debug for AscRef<T>
where
T: Debug,
{
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.debug_tuple("AscRef").field(&self.inner).finish()
}
}
impl<T> Deref for AscRef<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<T> ToOwned for AscRef<T>
where
T: Clone,
{
type Owned = AscBox<T>;
fn to_owned(&self) -> Self::Owned {
AscBox::new(self.inner.clone())
}
}
#[repr(transparent)]
pub struct AscSlice<T> {
inner: [T; 0],
}
impl<T> AscSlice<T> {
pub fn as_slice(&self) -> &[T] {
let this = self.inner.as_ptr();
let len = unsafe {
let header = AscHeader::for_data(this);
header.len::<T>()
};
unsafe { slice::from_raw_parts(this, len) }
}
}
impl<T> Borrow<[T]> for AscSlice<T> {
fn borrow(&self) -> &[T] {
self.as_slice()
}
}
impl<T> Debug for AscSlice<T>
where
T: Debug,
{
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.debug_tuple("AscSlice").field(&self.as_slice()).finish()
}
}
impl<T> Deref for AscSlice<T> {
type Target = [T];
fn deref(&self) -> &Self::Target {
self.as_slice()
}
}
impl<T> ToOwned for AscSlice<T>
where
T: Clone,
{
type Owned = AscBox<[T]>;
fn to_owned(&self) -> Self::Owned {
self.as_slice().iter().cloned().collect()
}
}
#[repr(C)]
struct AscHeader {
mm_info: usize,
gc_info: usize,
gc_info2: usize,
rt_id: u32,
rt_size: u32,
}
impl AscHeader {
unsafe fn for_data<'a, T>(data: *const T) -> &'a AscHeader {
let header_size = mem::size_of::<AscHeader>();
&*data.cast::<u8>().sub(header_size).cast::<AscHeader>()
}
fn len<T>(&self) -> usize {
(self.rt_size as usize) / mem::size_of::<T>()
}
}
fn alloc_box<T>(type_id: u32, len: usize) -> NonNull<T> {
let header_size = mem::size_of::<AscHeader>();
let data_layout = match Layout::array::<T>(len) {
Ok(value) => value,
Err(_) => alloc::handle_alloc_error(Layout::new::<T>()),
};
let (layout, offset) = {
let layout =
unsafe { Layout::from_size_align_unchecked(header_size, ALIGN).pad_to_align() };
let (layout, offset) = match layout.extend(data_layout) {
Ok(value) => value,
Err(_) => alloc::handle_alloc_error(data_layout),
};
let layout = layout.pad_to_align();
(layout, offset)
};
let root = unsafe { alloc::alloc(layout) };
if root.is_null() {
alloc::handle_alloc_error(layout);
}
unsafe {
let header = root.add(offset - header_size).cast::<AscHeader>();
ptr::addr_of_mut!((*header).mm_info).write(layout.size());
ptr::addr_of_mut!((*header).gc_info).write(layout.align());
ptr::addr_of_mut!((*header).gc_info2).write(offset);
ptr::addr_of_mut!((*header).rt_id).write(type_id);
ptr::addr_of_mut!((*header).rt_size).write(data_layout.size() as _);
}
unsafe { NonNull::new_unchecked(root.add(offset).cast()) }
}
unsafe fn drop_box<T>(data: NonNull<T>, len: usize) {
let (size, align, offset) = {
let header = AscHeader::for_data(data.as_ptr());
(header.mm_info, header.gc_info, header.gc_info2)
};
if mem::needs_drop::<T>() {
for i in 0..len {
let item = data.as_ptr().add(i);
ptr::drop_in_place(item);
}
}
let layout = Layout::from_size_align_unchecked(size, align);
let root = data.cast::<u8>().as_ptr().sub(offset);
alloc::dealloc(root, layout)
}