#![allow(clippy::cast_ptr_alignment, clippy::cast_possible_truncation)]
use core::{
marker::PhantomData,
ptr::NonNull,
sync::atomic::{AtomicU32, Ordering},
};
use osom_lib_alloc::traits::Allocator;
use osom_lib_primitives::length::Length;
use osom_lib_reprc::traits::ReprC;
use crate::errors::CArcArrayError;
use super::layout::CArcLayout;
#[repr(transparent)]
#[must_use]
#[derive(Debug)]
pub struct InternalArcArray<T, TAllocator: Allocator> {
ptr: *mut u8,
_phantom: PhantomData<(T, TAllocator)>,
}
unsafe impl<T: ReprC, TAllocator: Allocator> ReprC for InternalArcArray<T, TAllocator> {
const CHECK: () = {
let () = T::CHECK;
let () = TAllocator::CHECK;
};
}
unsafe impl<T: Send, TAllocator: Allocator + Send> Send for InternalArcArray<T, TAllocator> {}
unsafe impl<T: Sync, TAllocator: Allocator + Sync> Sync for InternalArcArray<T, TAllocator> {}
impl<T, TAllocator: Allocator> InternalArcArray<T, TAllocator> {
const LAYOUT: CArcLayout<T, TAllocator> = CArcLayout::new();
pub fn new(capacity: Length, mut allocator: TAllocator) -> Result<Self, CArcArrayError> {
let layout = Self::LAYOUT.calculate_for_capacity(capacity.as_u32())?;
let ptr = allocator
.allocate(layout)
.map_err(|_| CArcArrayError::AllocationError)?
.as_ptr();
let result = Self {
ptr,
_phantom: PhantomData,
};
unsafe {
result.allocator_ptr().write(allocator);
result.strong().store(1, Ordering::Relaxed);
result.weak().store(1, Ordering::Relaxed);
result.size_ptr().write(0);
result.capacity_ptr().write(capacity.as_u32());
}
Ok(result)
}
#[inline]
pub fn raw_equals(&self, other: &Self) -> bool {
core::ptr::eq(self.ptr, other.ptr)
}
pub fn shrink_to_fit(&mut self) -> Result<(), CArcArrayError> {
let current_size = self.size().as_u32();
let current_capacity = self.capacity().as_u32();
if current_size == current_capacity {
return Ok(());
}
let old_layout = Self::LAYOUT.calculate_for_capacity(current_capacity)?;
let new_layout = Self::LAYOUT.calculate_for_capacity(current_size)?;
unsafe {
let old_ptr = NonNull::new_unchecked(self.ptr);
let new_ptr = self
.allocator_ptr()
.as_mut_unchecked()
.resize(old_ptr, old_layout, new_layout)
.map_err(|_| CArcArrayError::AllocationError)?;
self.ptr = new_ptr.as_ptr();
self.capacity_ptr().write(current_size);
Ok(())
}
}
#[inline]
pub const fn strong(&self) -> &AtomicU32 {
unsafe { &*self.ptr.add(Self::LAYOUT.strong_offset).cast::<AtomicU32>() }
}
#[inline]
pub const fn weak(&self) -> &AtomicU32 {
unsafe { &*self.ptr.add(Self::LAYOUT.weak_offset).cast::<AtomicU32>() }
}
#[inline]
pub const fn size(&self) -> Length {
unsafe { Length::new_unchecked(self.size_ptr().read()) }
}
#[inline]
pub const fn capacity(&self) -> Length {
unsafe { Length::new_unchecked(self.capacity_ptr().read()) }
}
pub fn try_push_slice(&mut self, slice: &[T]) -> Result<(), CArcArrayError>
where
T: Clone,
{
let slice_len = slice.len();
if slice_len > Length::MAX_LENGTH.as_usize() {
return Err(CArcArrayError::ArraySizeOutOfRange);
}
let slice_len = slice_len as u64;
let current_size = self.size().as_u32();
if u64::from(current_size) + slice_len > u64::from(Length::MAX_LENGTH.as_u32()) {
return Err(CArcArrayError::ArraySizeOutOfRange);
}
let slice_len = slice_len as u32;
let new_size = unsafe { current_size.unchecked_add(slice_len) };
if new_size > self.capacity().as_u32() {
self.grow(new_size)?;
}
unsafe {
let mut data_ptr = self.data_ptr().add(current_size as usize);
let mut slice_ptr = slice.as_ptr();
for _ in 0..slice_len {
data_ptr.write(slice_ptr.as_ref_unchecked().clone());
data_ptr = data_ptr.add(1);
slice_ptr = slice_ptr.add(1);
}
self.size_ptr().write(new_size);
}
Ok(())
}
pub fn try_push_array<const N: usize>(&mut self, array: [T; N]) -> Result<(), CArcArrayError> {
if N > Length::MAX_LENGTH.as_usize() {
return Err(CArcArrayError::ArraySizeOutOfRange);
}
let slice_len = N as u64;
let current_size = self.size().as_u32();
if u64::from(current_size) + slice_len > u64::from(Length::MAX_LENGTH.as_u32()) {
return Err(CArcArrayError::ArraySizeOutOfRange);
}
let slice_len = slice_len as u32;
let new_size = unsafe { current_size.unchecked_add(slice_len) };
if new_size > self.capacity().as_u32() {
self.grow(new_size)?;
}
unsafe {
let data_ptr = self.data_ptr().add(current_size as usize);
data_ptr.copy_from_nonoverlapping(array.as_ptr(), N);
core::mem::forget(array);
self.size_ptr().write(new_size);
}
Ok(())
}
#[inline]
pub unsafe fn deallocate_memory(&self) {
unsafe {
let mut allocator = self.allocator_ptr().read();
let layout = Self::LAYOUT
.calculate_for_capacity(self.size().as_u32())
.expect("Layout should be calculatable");
allocator.deallocate(NonNull::new_unchecked(self.ptr), layout);
}
}
#[inline]
pub const fn raw_clone(&self) -> Self {
Self {
ptr: self.ptr,
_phantom: PhantomData,
}
}
#[inline(always)]
pub const fn raw_ptr(&self) -> *mut u8 {
self.ptr
}
#[inline(always)]
pub const fn from_raw_ptr(ptr: *mut u8) -> Self {
Self {
ptr,
_phantom: PhantomData,
}
}
#[inline]
pub const fn data_slice(&self) -> &[T] {
unsafe { core::slice::from_raw_parts(self.data_ptr(), self.size().as_usize()) }
}
#[inline]
pub const fn data_slice_mut(&mut self) -> &mut [T] {
unsafe { core::slice::from_raw_parts_mut(self.data_ptr(), self.size().as_usize()) }
}
fn grow(&mut self, new_size: u32) -> Result<(), CArcArrayError> {
let current_capacity = self.capacity().as_u32();
let mut new_capacity = current_capacity;
for _ in 0..4 {
new_capacity = 1 + 2 * new_capacity / 3;
if new_capacity >= new_size {
break;
}
}
if new_capacity < new_size {
new_capacity = new_size + 4;
if new_capacity > Length::MAX_LENGTH.as_u32() {
return Err(CArcArrayError::ArraySizeOutOfRange);
}
}
let old_layout = Self::LAYOUT.calculate_for_capacity(current_capacity)?;
let new_layout = Self::LAYOUT.calculate_for_capacity(new_capacity)?;
unsafe {
let allocator = self.allocator_ptr().as_mut_unchecked();
let new_ptr = allocator
.resize(NonNull::new_unchecked(self.ptr), old_layout, new_layout)
.map_err(|_| CArcArrayError::AllocationError)?;
let raw_ptr = new_ptr.as_ptr();
self.ptr = raw_ptr;
self.capacity_ptr().write(new_capacity);
};
Ok(())
}
#[inline]
const fn data_ptr(&self) -> *mut T {
unsafe { self.ptr.add(Self::LAYOUT.data_offset).cast::<T>() }
}
#[inline]
const fn allocator_ptr(&self) -> *mut TAllocator {
unsafe { self.ptr.add(Self::LAYOUT.allocator_offset).cast::<TAllocator>() }
}
#[inline]
const fn size_ptr(&self) -> *mut u32 {
unsafe { self.ptr.add(Self::LAYOUT.size_offset).cast::<u32>() }
}
#[inline]
const fn capacity_ptr(&self) -> *mut u32 {
unsafe { self.ptr.add(Self::LAYOUT.capacity_offset).cast::<u32>() }
}
}