use alloc::vec::Vec;
use core::marker::PhantomData;
use core::mem::{forget, ManuallyDrop, MaybeUninit};
use core::ops::{Deref, DerefMut, Range};
use core::panic::{RefUnwindSafe, UnwindSafe};
use core::ptr::NonNull;
use crate::backend::Backend;
use crate::smart::{Inner, Smart, UpdateResult};
const MASK: usize = super::MASK as usize;
const TAG: usize = super::TAG_ALLOCATED as usize;
struct TaggedSmart<B: Backend>(usize, PhantomData<Smart<Vec<u8>, B>>);
impl<B: Backend> Clone for TaggedSmart<B> {
#[cfg_attr(coverage_nightly, coverage(off))]
fn clone(&self) -> Self {
*self
}
}
impl<B: Backend> Copy for TaggedSmart<B> {}
impl<B: Backend> TaggedSmart<B> {
#[inline]
fn from(raw: Smart<Vec<u8>, B>) -> Self {
let ptr = raw.into_raw().as_ptr();
debug_assert!(ptr.is_aligned());
debug_assert!((ptr as usize) & MASK == 0);
let addr = ptr.map_addr(|addr| addr | TAG).expose_provenance();
Self(addr, PhantomData)
}
#[inline]
fn into(self) -> Smart<Vec<u8>, B> {
let this: Smart<Vec<u8>, B>;
debug_assert!(self.0 & MASK == TAG);
unsafe {
let new_ptr = core::ptr::with_exposed_provenance_mut::<Inner<Vec<u8>, B>>(self.0 ^ TAG);
debug_assert!(!new_ptr.is_null());
#[cfg(miri)]
let _ = &*new_ptr;
this = Smart::from_raw(NonNull::new_unchecked(new_ptr));
}
this
}
#[inline]
const fn check_tag(self) -> bool {
self.0 & MASK == TAG
}
fn explicit_clone(self) -> Self {
let r = ManuallyDrop::new(self.into());
Self::from((*r).clone())
}
}
#[repr(C)]
pub struct Allocated<B: Backend> {
#[cfg(target_endian = "little")]
owner: TaggedSmart<B>,
ptr: *const u8,
len: usize,
#[cfg(target_endian = "big")]
owner: TaggedSmart<B>,
}
impl<B: Backend> Copy for Allocated<B> {}
impl<B: Backend> Clone for Allocated<B> {
#[cfg_attr(coverage_nightly, coverage(off))]
fn clone(&self) -> Self {
*self
}
}
unsafe impl<B: Backend + Sync> Sync for Allocated<B> {}
unsafe impl<B: Backend + Send> Send for Allocated<B> {}
impl<B: Backend + Unpin> Unpin for Allocated<B> {}
impl<B: Backend + UnwindSafe> UnwindSafe for Allocated<B> {}
impl<B: Backend + RefUnwindSafe> RefUnwindSafe for Allocated<B> {}
impl<B: Backend> Allocated<B> {
fn into_owner(self) -> Smart<Vec<u8>, B> {
self.owner.into()
}
fn owner(&self) -> impl Deref<Target = Smart<Vec<u8>, B>> {
ManuallyDrop::new(self.into_owner())
}
unsafe fn owner_mut(&mut self) -> impl DerefMut<Target = Smart<Vec<u8>, B>> {
debug_assert!(self.is_unique());
ManuallyDrop::new(self.into_owner())
}
#[inline]
pub fn new(v: Vec<u8>) -> Self {
let ptr = v.as_ptr();
let len = v.len();
let owner = Smart::new(v);
let this = Self {
ptr,
len,
owner: TaggedSmart::from(owner),
};
debug_assert!(this.is_unique());
this
}
pub fn from_slice(slice: &[u8]) -> Self {
Self::new(slice.to_vec())
}
#[inline]
pub const fn len(&self) -> usize {
self.len
}
#[inline]
pub const fn as_slice(&self) -> &[u8] {
unsafe { core::slice::from_raw_parts(self.ptr, self.len) }
}
#[inline]
pub const fn as_ptr(&self) -> *const u8 {
self.ptr
}
#[inline]
#[allow(clippy::needless_pass_by_ref_mut)]
pub fn as_mut_ptr(&mut self) -> Option<*mut u8> {
if self.is_unique() {
Some(self.ptr.cast_mut())
} else {
None
}
}
#[inline]
#[allow(clippy::needless_pass_by_ref_mut)]
pub unsafe fn as_mut_ptr_unchecked(&mut self) -> *mut u8 {
debug_assert!(self.is_unique());
self.ptr.cast_mut()
}
#[inline]
pub fn as_mut_slice(&mut self) -> Option<&mut [u8]> {
if self.is_unique() {
Some(unsafe { self.as_mut_slice_unchecked() })
} else {
None
}
}
#[inline]
pub unsafe fn as_mut_slice_unchecked(&mut self) -> &mut [u8] {
debug_assert!(self.is_valid());
debug_assert!(self.is_unique());
unsafe { core::slice::from_raw_parts_mut(self.ptr.cast_mut(), self.len) }
}
#[inline]
pub unsafe fn slice_unchecked(&self, range: Range<usize>) -> Self {
debug_assert!(self.is_valid());
debug_assert!(range.start <= range.end);
debug_assert!(range.start <= self.len);
debug_assert!(range.end <= self.len);
let owner = self.owner.explicit_clone();
let ptr = unsafe { self.ptr.add(range.start) };
Self {
ptr,
len: range.len(),
owner,
}
}
#[inline]
pub fn explicit_clone(&self) -> Self {
debug_assert!(self.is_valid());
let owner = self.owner();
if owner.incr() == UpdateResult::Overflow {
Self::from_slice(self.as_slice())
} else {
*self
}
}
#[inline]
pub fn explicit_drop(self) {
debug_assert!(self.is_valid());
let _ = self.into_owner();
}
#[inline]
#[cfg_attr(coverage_nightly, coverage(off))]
pub fn is_valid(&self) -> bool {
if !self.owner.check_tag() {
return false;
}
let owner = self.owner();
let owner_ptr = owner.as_ptr();
let shift = unsafe { self.ptr.offset_from(owner_ptr) };
shift >= 0 && {
#[allow(clippy::cast_sign_loss)]
let shift = shift as usize;
shift <= owner.len() && shift + self.len <= owner.len()
}
}
#[inline]
pub fn capacity(&self) -> usize {
debug_assert!(self.is_valid());
self.owner().capacity()
}
#[inline]
pub fn try_into_vec(self) -> Result<Vec<u8>, Self> {
debug_assert!(self.is_valid());
let owner = self.owner();
if self.ptr != owner.as_ptr() {
return Err(self);
}
let len = self.len();
self.into_owner().try_unwrap().map_or_else(
|owner| {
forget(owner); Err(self)
},
|mut owner| {
owner.truncate(len);
Ok(owner)
},
)
}
pub fn is_unique(&self) -> bool {
debug_assert!(self.is_valid());
self.owner().is_unique()
}
pub unsafe fn push_slice_unchecked(&mut self, addition: &[u8]) {
debug_assert!(self.is_valid());
let mut owner = unsafe { self.owner_mut() };
let v = unsafe { owner.as_mut_unchecked() };
#[allow(clippy::cast_sign_loss)]
let shift = unsafe { self.ptr.offset_from(v.as_ptr()) as usize };
v.truncate(shift + self.len);
v.extend_from_slice(addition);
self.len += addition.len();
self.ptr = unsafe { v.as_ptr().add(shift) };
}
pub fn spare_capacity_mut(&mut self) -> &mut [MaybeUninit<u8>] {
debug_assert!(self.is_valid());
if !self.is_unique() {
return &mut [];
}
let mut owner = unsafe { self.owner_mut() };
let v = unsafe { owner.as_mut_unchecked_extended() };
#[allow(clippy::cast_sign_loss)]
let start = unsafe { self.ptr.offset_from(v.as_ptr()) as usize };
v.truncate(start + self.len);
v.spare_capacity_mut()
}
pub unsafe fn set_len(&mut self, new_len: usize) {
if new_len <= self.len {
self.len = new_len;
return;
}
debug_assert!(self.is_unique());
debug_assert!(new_len <= self.capacity());
let mut owner = unsafe { self.owner_mut() };
let v = unsafe { owner.as_mut_unchecked() };
#[allow(clippy::cast_sign_loss)]
let start = unsafe { self.ptr.offset_from(v.as_ptr()).abs_diff(0) };
unsafe { v.set_len(start + new_len) };
self.len = new_len;
}
pub fn shrink_to(&mut self, min_capacity: usize) {
let min_capacity = min_capacity.max(self.len);
if self.capacity() <= min_capacity {
return;
}
let mut new_vec = Vec::with_capacity(min_capacity);
new_vec.extend_from_slice(self.as_slice());
let old = core::mem::replace(self, Self::new(new_vec));
old.explicit_drop();
}
}
#[cfg(test)]
mod tests {
use alloc::vec;
use super::Allocated;
use crate::Rc;
#[test]
fn test_alloc() {
let allocated = Allocated::<Rc>::new(vec![]);
let _ = allocated.explicit_drop();
}
#[test]
#[cfg_attr(coverage_nightly, coverage(off))]
fn test_try_into_vec() {
let allocated = Allocated::<Rc>::new(vec![0, 1, 2]);
assert_eq!(allocated.owner().ref_count(), 1);
{
let slice = unsafe { allocated.slice_unchecked(1..2) };
assert_eq!(allocated.owner().ref_count(), 2);
let Err(allocated) = slice.try_into_vec() else {
panic!("shared reference cannot be converted to vec")
};
allocated.explicit_drop();
}
assert_eq!(allocated.owner().ref_count(), 1);
assert!(allocated.try_into_vec().is_ok());
}
}