use core::alloc::{GlobalAlloc, Layout};
use core::any::Any;
use core::cmp::Ordering;
use core::hash::{Hash, Hasher};
use core::mem::{self, align_of, size_of, MaybeUninit};
use core::ops::Deref;
use core::result::Result;
use std::alloc::{handle_alloc_error, System};
use std::borrow::Borrow;
use std::fmt;
#[repr(C)]
struct Bucket<T: ?Sized> {
count: usize,
size: usize,
val: T,
}
impl<T> From<T> for Bucket<T> {
fn from(val: T) -> Self {
debug_assert_eq!(align_of::<usize>(), align_of::<Self>());
Self {
count: 1,
size: size_of::<Self>(),
val,
}
}
}
impl<T: ?Sized> Bucket<T> {
unsafe fn count(val: &mut T) -> &mut usize {
let ptr: *mut T = val;
let ptr: *mut usize = ptr.cast();
let ptr = ptr.sub(2);
&mut *ptr
}
unsafe fn size(ptr: *const T) -> usize {
let ptr: *const usize = ptr.cast();
let ptr = ptr.sub(1);
*ptr
}
unsafe fn dealloc_ptr(ptr: *mut T) -> *mut u8 {
let ptr: *mut usize = ptr.cast();
let ptr = ptr.sub(2);
ptr as *mut u8
}
}
pub struct Sc<T: ?Sized, A = System>
where
A: GlobalAlloc,
{
ptr: *mut T,
alloc: A,
}
impl<T: ?Sized, A> Drop for Sc<T, A>
where
A: GlobalAlloc,
{
fn drop(&mut self) {
unsafe {
let count = Bucket::count(&mut *self.ptr);
*count -= 1;
if *count == 0 {
let layout =
Layout::from_size_align(Bucket::size(self.ptr), align_of::<usize>()).unwrap();
let ptr = Bucket::dealloc_ptr(self.ptr);
self.ptr.drop_in_place();
self.alloc.dealloc(ptr, layout);
}
}
}
}
impl<T, A> Default for Sc<T, A>
where
T: Default,
A: Default + GlobalAlloc,
{
fn default() -> Self {
Self::new(T::default(), A::default())
}
}
impl<T, A> From<T> for Sc<T, A>
where
A: Default + GlobalAlloc,
{
fn from(val: T) -> Self {
Self::new(val, A::default())
}
}
impl<T, A> From<&'_ [T]> for Sc<[T], A>
where
T: Clone,
A: Default + GlobalAlloc,
{
fn from(vals: &'_ [T]) -> Self {
Sc::<T, A>::from_slice_alloc(vals, A::default())
}
}
impl<T: ?Sized, A> Clone for Sc<T, A>
where
A: Clone + GlobalAlloc,
{
fn clone(&self) -> Self {
unsafe {
let val = &mut *self.ptr;
*Bucket::count(val) += 1;
};
Self {
ptr: self.ptr,
alloc: self.alloc.clone(),
}
}
}
impl<T, A> Sc<T, A>
where
A: GlobalAlloc,
{
pub fn new(val: T, alloc: A) -> Self {
let bucket = unsafe {
let layout = Layout::new::<Bucket<T>>();
let ptr = alloc.alloc(layout) as *mut Bucket<T>;
if ptr.is_null() {
handle_alloc_error(layout);
}
ptr.write(Bucket::from(val));
&mut *ptr
};
Self {
ptr: &mut bucket.val,
alloc,
}
}
pub fn from_slice_alloc(vals: &[T], alloc: A) -> Sc<[T], A>
where
T: Clone,
{
unsafe {
let layout = {
let align = align_of::<Bucket<T>>();
let size = size_of::<Bucket<T>>() - size_of::<Bucket<T>>()
+ vals.len() * size_of::<Bucket<T>>();
Layout::from_size_align_unchecked(size, align)
};
let ptr = alloc.alloc(layout) as *mut Bucket<T>;
if ptr.is_null() {
handle_alloc_error(layout);
}
let mut bucket = &mut *ptr;
bucket.count = 1;
bucket.size = layout.size();
let ptr = &mut bucket.val as *mut T;
for i in 0..vals.len() {
let v = vals[i].clone();
let ptr = ptr.add(i);
ptr.write(v);
}
let slice_ref = core::slice::from_raw_parts_mut(ptr, vals.len());
Sc::<[T], A> {
ptr: &mut *slice_ref,
alloc,
}
}
}
}
impl<A> Sc<dyn Any, A>
where
A: GlobalAlloc,
{
pub fn new_any<T>(val: T, alloc: A) -> Self
where
T: Any,
{
let bucket = unsafe {
let layout = Layout::new::<Bucket<T>>();
let ptr = alloc.alloc(layout) as *mut Bucket<T>;
if ptr.is_null() {
handle_alloc_error(layout);
}
ptr.write(Bucket::from(val));
&mut *ptr
};
Self {
ptr: &mut bucket.val,
alloc,
}
}
}
impl<T: ?Sized, A> fmt::Debug for Sc<T, A>
where
A: fmt::Debug + GlobalAlloc,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let ptr = format!("{:p}", self.ptr);
let alloc = format!("{:?}", self.alloc);
f.debug_struct("Sc")
.field("ptr", &ptr)
.field("alloc", &alloc)
.finish()
}
}
impl<T: ?Sized, A> PartialEq for Sc<T, A>
where
T: PartialEq,
A: GlobalAlloc,
{
fn eq(&self, other: &Self) -> bool {
let this: &T = self.borrow();
let other: &T = other.borrow();
this.eq(other)
}
}
impl<T: ?Sized, A> Eq for Sc<T, A>
where
T: Eq,
A: GlobalAlloc,
{
}
impl<T: ?Sized, A> PartialOrd for Sc<T, A>
where
T: PartialOrd,
A: GlobalAlloc,
{
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
let this: &T = self.borrow();
let other: &T = other.borrow();
this.partial_cmp(other)
}
}
impl<T: ?Sized, A> Ord for Sc<T, A>
where
T: Ord,
A: GlobalAlloc,
{
fn cmp(&self, other: &Self) -> Ordering {
let this: &T = self.borrow();
let other: &T = other.borrow();
this.cmp(other)
}
}
impl<T: ?Sized, A> Hash for Sc<T, A>
where
T: Hash,
A: GlobalAlloc,
{
fn hash<H>(&self, hasher: &mut H)
where
H: Hasher,
{
let inner: &T = self.borrow();
inner.hash(hasher);
}
}
impl<T: ?Sized, A> fmt::Display for Sc<T, A>
where
T: fmt::Display,
A: GlobalAlloc,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let inner: &T = self.deref();
fmt::Display::fmt(inner, f)
}
}
impl<T: ?Sized, A> AsRef<T> for Sc<T, A>
where
A: GlobalAlloc,
{
fn as_ref(&self) -> &T {
self.deref()
}
}
impl<T: ?Sized, A> Borrow<T> for Sc<T, A>
where
A: GlobalAlloc,
{
fn borrow(&self) -> &T {
self.deref()
}
}
impl<T: ?Sized, A> Deref for Sc<T, A>
where
A: GlobalAlloc,
{
type Target = T;
fn deref(&self) -> &Self::Target {
unsafe { &*self.ptr }
}
}
impl<T, A> Sc<T, A>
where
A: GlobalAlloc,
{
pub fn to_any(this: Self) -> Sc<dyn Any, A>
where
T: Any,
{
let (ptr, alloc) = Self::decouple(this);
Sc::<dyn Any, A> { ptr, alloc }
}
}
impl<T: ?Sized, A> Sc<T, A>
where
A: GlobalAlloc,
{
pub fn as_ptr(this: &Self) -> *const T {
this.ptr
}
pub fn count(this: &Self) -> usize {
unsafe {
let val = &mut *this.ptr;
*Bucket::count(val)
}
}
pub fn get_mut(this: &mut Self) -> Option<&mut T> {
if Self::count(this) == 1 {
Some(unsafe { &mut *this.ptr })
} else {
None
}
}
pub fn ptr_eq(this: &Self, other: &Self) -> bool {
Sc::as_ptr(this) == Sc::as_ptr(other)
}
pub fn into_raw_alloc(this: Self) -> (*const T, A) {
let (ptr, alloc) = Self::decouple(this);
(ptr, alloc)
}
pub unsafe fn from_raw_alloc(ptr: *const T, alloc: A) -> Self {
Self {
ptr: ptr as *mut T,
alloc,
}
}
fn decouple(this: Self) -> (*mut T, A) {
let alloc = unsafe {
let mut alloc = MaybeUninit::<A>::uninit();
let ptr = alloc.as_mut_ptr();
ptr.copy_from_nonoverlapping(&this.alloc, 1);
alloc.assume_init()
};
let ret = (this.ptr, alloc);
mem::forget(this);
ret
}
}
impl<T: Clone, A: Clone> Sc<T, A>
where
A: GlobalAlloc,
{
pub fn make_mut(this: &mut Self) -> &mut T {
if Self::count(this) != 1 {
let val: &T = this.deref();
*this = Sc::new(val.clone(), this.alloc.clone());
}
unsafe { &mut *this.ptr }
}
}
impl<A> Sc<dyn Any, A>
where
A: GlobalAlloc,
{
pub fn downcast<T: Any>(self) -> Result<Sc<T, A>, Self> {
let val: &mut dyn Any = unsafe { &mut *self.ptr };
match val.downcast_mut() {
None => Err(self),
Some(t) => {
let (_, alloc) = Self::decouple(self);
Ok(Sc::<T, A> {
ptr: t as *mut T,
alloc,
})
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use gharial::{GAlloc, GBox};
#[test]
fn new() {
let five = GBox::from(5);
let _five = Sc::new(five, GAlloc::default());
}
#[test]
fn clone() {
let five = GBox::from(5);
let five = Sc::new(five, GAlloc::default());
let _cloned = five.clone();
}
#[test]
fn make_mut() {
let inner = GBox::from(5);
let mut data = Sc::new(inner, GAlloc::default());
{
let _mr = Sc::make_mut(&mut data);
}
let _data2 = data.clone();
{
let _mr = Sc::make_mut(&mut data);
}
}
#[test]
fn downcast() {
let inner = GBox::from(8);
let sc = Sc::new_any(inner, GAlloc::default());
let ok = Sc::downcast::<GBox<i32>>(sc.clone());
assert_eq!(true, ok.is_ok());
let fail = Sc::downcast::<String>(sc.clone());
assert_eq!(true, fail.is_err());
}
#[test]
fn to_any() {
let inner = GBox::from(6);
let sc = Sc::new(inner, GAlloc::default());
let _any = Sc::to_any(sc);
}
#[test]
fn from_slice_alloc() {
let inners: [GBox<i32>; 2] = [GBox::from(6), GBox::from(4)];
let _sc = Sc::from_slice_alloc(&inners, GAlloc::default());
}
#[test]
fn raw_alloc() {
let sc: Sc<GBox<i32>> = Sc::from(GBox::from(0));
let (ptr, alloc) = Sc::into_raw_alloc(sc);
let _sc = unsafe { Sc::from_raw_alloc(ptr, alloc) };
}
}