use std::alloc::{alloc, dealloc, handle_alloc_error, Layout};
use std::borrow::Borrow;
use std::cmp::Ordering;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::marker::PhantomData;
use std::mem::{self, ManuallyDrop};
use std::ops::Deref;
use std::ptr::{self, NonNull};
use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering};
use super::CompactVec;
#[repr(C)]
struct Header {
count: AtomicUsize,
len: usize,
}
#[inline]
const fn data_offset_for<T>() -> usize {
let header_size = mem::size_of::<Header>();
let align = mem::align_of::<T>();
(header_size + align - 1) & !(align - 1)
}
pub unsafe trait CompactArcDrop {
unsafe fn drop_and_dealloc(ptr: *mut u8);
}
unsafe impl<T> CompactArcDrop for T {
#[inline]
unsafe fn drop_and_dealloc(ptr: *mut u8) {
let data_offset = data_offset_for::<T>();
let align = mem::align_of::<T>().max(mem::align_of::<Header>());
let data_ptr = ptr.add(data_offset) as *mut T;
ptr::drop_in_place(data_ptr);
let layout = Layout::from_size_align_unchecked(data_offset + mem::size_of::<T>(), align);
dealloc(ptr, layout);
}
}
unsafe impl CompactArcDrop for str {
#[inline]
unsafe fn drop_and_dealloc(ptr: *mut u8) {
let header = ptr as *mut Header;
let len = (*header).len;
let data_offset = data_offset_for::<u8>(); let total_size = data_offset + len;
let layout = Layout::from_size_align_unchecked(total_size, mem::align_of::<Header>());
dealloc(ptr, layout);
}
}
#[allow(clippy::manual_slice_size_calculation)]
unsafe impl<T> CompactArcDrop for [T] {
#[inline]
unsafe fn drop_and_dealloc(ptr: *mut u8) {
let header = ptr as *mut Header;
let len = (*header).len;
let data_offset = data_offset_for::<T>();
let align = mem::align_of::<T>().max(mem::align_of::<Header>());
let data_ptr = ptr.add(data_offset) as *mut T;
ptr::drop_in_place(std::ptr::slice_from_raw_parts_mut(data_ptr, len));
let layout =
Layout::from_size_align_unchecked(data_offset + mem::size_of::<T>() * len, align);
dealloc(ptr, layout);
}
}
pub struct CompactArc<T: ?Sized + CompactArcDrop> {
ptr: NonNull<Header>,
_marker: PhantomData<T>,
}
unsafe impl<T: ?Sized + CompactArcDrop + Send + Sync> Send for CompactArc<T> {}
unsafe impl<T: ?Sized + CompactArcDrop + Send + Sync> Sync for CompactArc<T> {}
impl<T: ?Sized + CompactArcDrop> Drop for CompactArc<T> {
#[inline]
fn drop(&mut self) {
let header = self.ptr.as_ptr();
let old_count = unsafe { (*header).count.fetch_sub(1, AtomicOrdering::Release) };
if old_count == 1 {
std::sync::atomic::fence(AtomicOrdering::Acquire);
unsafe {
T::drop_and_dealloc(header as *mut u8);
}
}
}
}
impl<T: ?Sized + CompactArcDrop> Clone for CompactArc<T> {
#[inline]
fn clone(&self) -> Self {
let header = self.ptr.as_ptr();
let old_count = unsafe { (*header).count.fetch_add(1, AtomicOrdering::Relaxed) };
if old_count > isize::MAX as usize {
std::process::abort();
}
CompactArc {
ptr: self.ptr,
_marker: PhantomData,
}
}
}
impl<T: ?Sized + CompactArcDrop> CompactArc<T> {
#[inline]
pub fn ptr_eq(this: &Self, other: &Self) -> bool {
ptr::addr_eq(this.ptr.as_ptr(), other.ptr.as_ptr())
}
#[inline]
pub fn strong_count(this: &Self) -> usize {
unsafe { (*this.ptr.as_ptr()).count.load(AtomicOrdering::Relaxed) }
}
#[inline]
fn is_unique(this: &Self) -> bool {
unsafe { (*this.ptr.as_ptr()).count.load(AtomicOrdering::Acquire) == 1 }
}
#[inline]
pub fn meta(this: &Self) -> usize {
unsafe { (*this.ptr.as_ptr()).len }
}
}
impl<T: CompactArcDrop> CompactArc<T> {
#[inline]
#[must_use]
pub fn new(data: T) -> Self {
Self::new_with_meta(data, 0)
}
#[inline]
#[must_use]
pub fn new_with_meta(data: T, meta: usize) -> Self {
let data_offset = data_offset_for::<T>();
let align = mem::align_of::<T>().max(mem::align_of::<Header>());
let total_size = data_offset + mem::size_of::<T>();
let layout = Layout::from_size_align(total_size, align).expect("layout overflow");
unsafe {
let ptr = alloc(layout);
if ptr.is_null() {
handle_alloc_error(layout);
}
let header = ptr as *mut Header;
ptr::write(
header,
Header {
count: AtomicUsize::new(1),
len: meta,
},
);
let data_ptr = ptr.add(data_offset) as *mut T;
ptr::write(data_ptr, data);
CompactArc {
ptr: NonNull::new_unchecked(header),
_marker: PhantomData,
}
}
}
#[inline]
pub fn try_unwrap(this: Self) -> Result<T, Self> {
let header = this.ptr.as_ptr();
if unsafe {
(*header)
.count
.compare_exchange(1, 0, AtomicOrdering::Acquire, AtomicOrdering::Relaxed)
.is_ok()
} {
let _ = ManuallyDrop::new(this);
unsafe {
let data_offset = data_offset_for::<T>();
let data_ptr = (header as *const u8).add(data_offset) as *const T;
let data = ptr::read(data_ptr);
let align = mem::align_of::<T>().max(mem::align_of::<Header>());
let layout =
Layout::from_size_align_unchecked(data_offset + mem::size_of::<T>(), align);
dealloc(header as *mut u8, layout);
Ok(data)
}
} else {
Err(this)
}
}
#[inline]
pub fn get_mut(this: &mut Self) -> Option<&mut T> {
if Self::is_unique(this) {
unsafe {
let data_ptr = (this.ptr.as_ptr() as *mut u8).add(data_offset_for::<T>()) as *mut T;
Some(&mut *data_ptr)
}
} else {
None
}
}
#[inline]
pub fn make_mut(this: &mut Self) -> &mut T
where
T: Clone,
{
if !Self::is_unique(this) {
let meta = Self::meta(this);
*this = CompactArc::new_with_meta((**this).clone(), meta);
}
Self::get_mut(this).unwrap()
}
#[inline]
pub fn as_ptr(this: &Self) -> *const T {
let header = this.ptr.as_ptr();
unsafe { (header as *const u8).add(data_offset_for::<T>()) as *const T }
}
#[inline]
pub fn into_raw(this: Self) -> *const T {
let header = this.ptr.as_ptr();
let ptr = unsafe { (header as *const u8).add(data_offset_for::<T>()) as *const T };
mem::forget(this);
ptr
}
#[inline]
pub unsafe fn from_raw(ptr: *const T) -> Self {
let header = (ptr as *const u8).sub(data_offset_for::<T>()) as *mut Header;
CompactArc {
ptr: NonNull::new_unchecked(header),
_marker: PhantomData,
}
}
}
impl<T: CompactArcDrop> Deref for CompactArc<T> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
unsafe {
let data_ptr = (self.ptr.as_ptr() as *const u8).add(data_offset_for::<T>()) as *const T;
&*data_ptr
}
}
}
impl<T: CompactArcDrop + Default> Default for CompactArc<T> {
#[inline]
fn default() -> Self {
CompactArc::new(T::default())
}
}
impl<T: CompactArcDrop + fmt::Debug> fmt::Debug for CompactArc<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&**self, f)
}
}
impl<T: CompactArcDrop + fmt::Display> fmt::Display for CompactArc<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&**self, f)
}
}
impl<T: CompactArcDrop + PartialEq> PartialEq for CompactArc<T> {
#[inline]
fn eq(&self, other: &Self) -> bool {
if CompactArc::ptr_eq(self, other) {
return true;
}
**self == **other
}
}
impl<T: CompactArcDrop + Eq> Eq for CompactArc<T> {}
impl<T: CompactArcDrop + PartialOrd> PartialOrd for CompactArc<T> {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
(**self).partial_cmp(&**other)
}
}
impl<T: CompactArcDrop + Ord> Ord for CompactArc<T> {
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
(**self).cmp(&**other)
}
}
impl<T: CompactArcDrop + Hash> Hash for CompactArc<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
(**self).hash(state)
}
}
impl<T: CompactArcDrop> Borrow<T> for CompactArc<T> {
fn borrow(&self) -> &T {
self
}
}
impl<T: CompactArcDrop> AsRef<T> for CompactArc<T> {
fn as_ref(&self) -> &T {
self
}
}
impl<T: CompactArcDrop> From<T> for CompactArc<T> {
#[inline]
fn from(value: T) -> Self {
CompactArc::new(value)
}
}
impl CompactArc<str> {
#[must_use]
pub fn from_str_slice(s: &str) -> Self {
let len = s.len();
let data_offset = data_offset_for::<u8>(); let total_size = data_offset + len;
let layout = Layout::from_size_align(total_size, mem::align_of::<Header>())
.expect("layout overflow");
unsafe {
let ptr = alloc(layout);
if ptr.is_null() {
handle_alloc_error(layout);
}
let header = ptr as *mut Header;
ptr::write(
header,
Header {
count: AtomicUsize::new(1),
len,
},
);
let data_ptr = ptr.add(data_offset);
ptr::copy_nonoverlapping(s.as_ptr(), data_ptr, len);
CompactArc {
ptr: NonNull::new_unchecked(header),
_marker: PhantomData,
}
}
}
#[inline]
pub fn len(&self) -> usize {
unsafe { (*self.ptr.as_ptr()).len }
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
impl Deref for CompactArc<str> {
type Target = str;
#[inline]
fn deref(&self) -> &str {
unsafe {
let header = self.ptr.as_ptr();
let len = (*header).len;
let data_offset = data_offset_for::<u8>(); let data_ptr = (header as *const u8).add(data_offset);
std::str::from_utf8_unchecked(std::slice::from_raw_parts(data_ptr, len))
}
}
}
impl From<&str> for CompactArc<str> {
#[inline]
fn from(s: &str) -> Self {
CompactArc::from_str_slice(s)
}
}
impl From<String> for CompactArc<str> {
#[inline]
fn from(s: String) -> Self {
CompactArc::from_str_slice(&s)
}
}
impl fmt::Debug for CompactArc<str> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&**self, f)
}
}
impl fmt::Display for CompactArc<str> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&**self, f)
}
}
impl PartialEq for CompactArc<str> {
#[inline]
fn eq(&self, other: &Self) -> bool {
if CompactArc::ptr_eq(self, other) {
return true;
}
**self == **other
}
}
impl Eq for CompactArc<str> {}
impl PartialOrd for CompactArc<str> {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for CompactArc<str> {
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
(**self).cmp(&**other)
}
}
impl Hash for CompactArc<str> {
fn hash<H: Hasher>(&self, state: &mut H) {
(**self).hash(state)
}
}
impl Borrow<str> for CompactArc<str> {
fn borrow(&self) -> &str {
self
}
}
impl AsRef<str> for CompactArc<str> {
fn as_ref(&self) -> &str {
self
}
}
impl<T> CompactArc<[T]> {
#[must_use]
pub fn from_vec(mut vec: Vec<T>) -> Self {
let len = vec.len();
let data_offset = data_offset_for::<T>();
let align = mem::align_of::<T>().max(mem::align_of::<Header>());
let data_size = mem::size_of::<T>() * len;
let layout =
Layout::from_size_align(data_offset + data_size, align).expect("layout overflow");
unsafe {
let ptr = alloc(layout);
if ptr.is_null() {
handle_alloc_error(layout);
}
let header = ptr as *mut Header;
ptr::write(
header,
Header {
count: AtomicUsize::new(1),
len,
},
);
let data_ptr = ptr.add(data_offset) as *mut T;
ptr::copy_nonoverlapping(vec.as_ptr(), data_ptr, len);
vec.set_len(0);
CompactArc {
ptr: NonNull::new_unchecked(header),
_marker: PhantomData,
}
}
}
#[must_use]
pub fn from_compact_vec(mut vec: CompactVec<T>) -> Self {
let len = vec.len();
let data_offset = data_offset_for::<T>();
let align = mem::align_of::<T>().max(mem::align_of::<Header>());
let data_size = mem::size_of::<T>() * len;
let layout =
Layout::from_size_align(data_offset + data_size, align).expect("layout overflow");
unsafe {
let ptr = alloc(layout);
if ptr.is_null() {
handle_alloc_error(layout);
}
let header = ptr as *mut Header;
ptr::write(
header,
Header {
count: AtomicUsize::new(1),
len,
},
);
let data_ptr = ptr.add(data_offset) as *mut T;
ptr::copy_nonoverlapping(vec.as_ptr(), data_ptr, len);
vec.set_len(0);
CompactArc {
ptr: NonNull::new_unchecked(header),
_marker: PhantomData,
}
}
}
}
impl<T: Clone> CompactArc<[T]> {
#[must_use]
pub fn from_slice(slice: &[T]) -> Self {
let len = slice.len();
let data_offset = data_offset_for::<T>();
let align = mem::align_of::<T>().max(mem::align_of::<Header>());
let layout = Layout::from_size_align(data_offset + mem::size_of_val(slice), align)
.expect("layout overflow");
unsafe {
let ptr = alloc(layout);
if ptr.is_null() {
handle_alloc_error(layout);
}
let header = ptr as *mut Header;
ptr::write(
header,
Header {
count: AtomicUsize::new(1),
len,
},
);
let data_ptr = ptr.add(data_offset) as *mut T;
struct CloneGuard<T> {
data_ptr: *mut T,
alloc_ptr: *mut u8,
layout: Layout,
written: usize,
}
impl<T> Drop for CloneGuard<T> {
fn drop(&mut self) {
unsafe {
let slice = ptr::slice_from_raw_parts_mut(self.data_ptr, self.written);
ptr::drop_in_place(slice);
dealloc(self.alloc_ptr, self.layout);
}
}
}
let mut guard = CloneGuard {
data_ptr,
alloc_ptr: ptr,
layout,
written: 0,
};
for (i, item) in slice.iter().enumerate() {
ptr::write(data_ptr.add(i), item.clone());
guard.written += 1;
}
mem::forget(guard);
CompactArc {
ptr: NonNull::new_unchecked(header),
_marker: PhantomData,
}
}
}
}
impl<T> CompactArc<[T]> {
#[inline]
pub fn len(&self) -> usize {
unsafe { (*self.ptr.as_ptr()).len }
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[inline]
pub(crate) fn data_ptr_mut(&self) -> *mut T {
unsafe { (self.ptr.as_ptr() as *mut u8).add(data_offset_for::<T>()) as *mut T }
}
}
impl<T> Deref for CompactArc<[T]> {
type Target = [T];
#[inline]
fn deref(&self) -> &[T] {
unsafe {
let header = self.ptr.as_ptr();
let len = (*header).len;
let data_ptr = (header as *const u8).add(data_offset_for::<T>()) as *const T;
std::slice::from_raw_parts(data_ptr, len)
}
}
}
impl<T: Clone> From<&[T]> for CompactArc<[T]> {
#[inline]
fn from(slice: &[T]) -> Self {
CompactArc::from_slice(slice)
}
}
impl<T> From<Vec<T>> for CompactArc<[T]> {
#[inline]
fn from(vec: Vec<T>) -> Self {
CompactArc::from_vec(vec)
}
}
impl<T: fmt::Debug> fmt::Debug for CompactArc<[T]> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&**self, f)
}
}
impl<T: PartialEq> PartialEq for CompactArc<[T]> {
#[inline]
fn eq(&self, other: &Self) -> bool {
if CompactArc::ptr_eq(self, other) {
return true;
}
**self == **other
}
}
impl<T: Eq> Eq for CompactArc<[T]> {}
impl<T: PartialOrd> PartialOrd for CompactArc<[T]> {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
(**self).partial_cmp(&**other)
}
}
impl<T: Ord> Ord for CompactArc<[T]> {
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
(**self).cmp(&**other)
}
}
impl<T: Hash> Hash for CompactArc<[T]> {
fn hash<H: Hasher>(&self, state: &mut H) {
(**self).hash(state)
}
}
impl<T> Borrow<[T]> for CompactArc<[T]> {
fn borrow(&self) -> &[T] {
self
}
}
impl<T> AsRef<[T]> for CompactArc<[T]> {
fn as_ref(&self) -> &[T] {
self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_and_deref() {
let arc = CompactArc::new(42);
assert_eq!(*arc, 42);
}
#[test]
fn test_clone_and_count() {
let arc = CompactArc::new(42);
assert_eq!(CompactArc::strong_count(&arc), 1);
let arc2 = arc.clone();
assert_eq!(CompactArc::strong_count(&arc), 2);
assert_eq!(CompactArc::strong_count(&arc2), 2);
drop(arc2);
assert_eq!(CompactArc::strong_count(&arc), 1);
}
#[test]
fn test_ptr_eq() {
let arc1 = CompactArc::new(42);
let arc2 = arc1.clone();
let arc3 = CompactArc::new(42);
assert!(CompactArc::ptr_eq(&arc1, &arc2));
assert!(!CompactArc::ptr_eq(&arc1, &arc3));
}
#[test]
fn test_try_unwrap_success() {
let arc = CompactArc::new(42);
let value = CompactArc::try_unwrap(arc).unwrap();
assert_eq!(value, 42);
}
#[test]
fn test_try_unwrap_failure() {
let arc = CompactArc::new(42);
let _arc2 = arc.clone();
let result = CompactArc::try_unwrap(arc);
assert!(result.is_err());
}
#[test]
fn test_get_mut() {
let mut arc = CompactArc::new(42);
*CompactArc::get_mut(&mut arc).unwrap() = 100;
assert_eq!(*arc, 100);
let _arc2 = arc.clone();
assert!(CompactArc::get_mut(&mut arc).is_none());
}
#[test]
fn test_make_mut() {
let mut arc = CompactArc::new(42);
*CompactArc::make_mut(&mut arc) = 100;
assert_eq!(*arc, 100);
let arc2 = arc.clone();
*CompactArc::make_mut(&mut arc) = 200;
assert_eq!(*arc, 200);
assert_eq!(*arc2, 100);
}
#[test]
fn test_into_raw_from_raw() {
let arc = CompactArc::new(42);
let ptr = CompactArc::into_raw(arc);
let arc2 = unsafe { CompactArc::from_raw(ptr) };
assert_eq!(*arc2, 42);
}
#[test]
fn test_debug_display() {
let arc = CompactArc::new(42);
assert_eq!(format!("{:?}", arc), "42");
assert_eq!(format!("{}", arc), "42");
}
#[test]
fn test_equality() {
let arc1 = CompactArc::new(42);
let arc2 = CompactArc::new(42);
let arc3 = CompactArc::new(100);
assert_eq!(arc1, arc2);
assert_ne!(arc1, arc3);
}
#[test]
fn test_ordering() {
let arc1 = CompactArc::new(1);
let arc2 = CompactArc::new(2);
assert!(arc1 < arc2);
assert!(arc2 > arc1);
}
#[test]
fn test_hash() {
use std::collections::HashMap;
let arc = CompactArc::new(42);
let mut map = HashMap::new();
map.insert(arc.clone(), "value");
assert_eq!(map.get(&arc), Some(&"value"));
}
#[test]
fn test_send_sync() {
fn assert_send<T: Send>() {}
fn assert_sync<T: Sync>() {}
assert_send::<CompactArc<i32>>();
assert_sync::<CompactArc<i32>>();
}
#[test]
fn test_sized_pointer_size() {
assert_eq!(std::mem::size_of::<CompactArc<i32>>(), 8);
assert_eq!(std::mem::size_of::<CompactArc<i64>>(), 8);
assert_eq!(std::mem::size_of::<CompactArc<String>>(), 8);
}
#[test]
fn test_header_size() {
assert_eq!(std::mem::size_of::<Header>(), 16);
}
#[test]
fn test_high_alignment_type() {
#[repr(align(64))]
#[derive(Debug, Clone, PartialEq)]
struct Aligned64 {
value: u64,
}
let arc = CompactArc::new(Aligned64 { value: 42 });
assert_eq!(arc.value, 42);
let data_ptr = CompactArc::as_ptr(&arc);
assert_eq!(data_ptr as usize % 64, 0, "Data should be 64-byte aligned");
let arc2 = arc.clone();
assert_eq!(arc2.value, 42);
assert_eq!(CompactArc::strong_count(&arc), 2);
drop(arc);
assert_eq!(arc2.value, 42);
let value = CompactArc::try_unwrap(arc2).unwrap();
assert_eq!(value.value, 42);
}
#[test]
fn test_high_alignment_slice() {
#[repr(align(32))]
#[derive(Debug, Clone, PartialEq)]
struct Aligned32(u32);
let arr: CompactArc<[Aligned32]> =
CompactArc::from_slice(&[Aligned32(1), Aligned32(2), Aligned32(3)]);
assert_eq!(arr.len(), 3);
assert_eq!(arr[0], Aligned32(1));
assert_eq!(arr[1], Aligned32(2));
assert_eq!(arr[2], Aligned32(3));
let first_ptr = &arr[0] as *const Aligned32;
assert_eq!(
first_ptr as usize % 32,
0,
"Elements should be 32-byte aligned"
);
}
#[test]
fn test_drop_complex_type() {
use std::sync::atomic::{AtomicUsize, Ordering};
static DROP_COUNT: AtomicUsize = AtomicUsize::new(0);
struct DropCounter;
impl Drop for DropCounter {
fn drop(&mut self) {
DROP_COUNT.fetch_add(1, Ordering::SeqCst);
}
}
DROP_COUNT.store(0, Ordering::SeqCst);
{
let arc = CompactArc::new(DropCounter);
let _arc2 = arc.clone();
let _arc3 = arc.clone();
}
assert_eq!(DROP_COUNT.load(Ordering::SeqCst), 1);
}
#[test]
fn test_thread_safety() {
use std::thread;
let arc = CompactArc::new(0);
let mut handles = vec![];
for _ in 0..10 {
let arc_clone = arc.clone();
handles.push(thread::spawn(move || {
for _ in 0..1000 {
let _ = *arc_clone;
let _another = arc_clone.clone();
}
}));
}
for handle in handles {
handle.join().unwrap();
}
drop(arc);
}
#[test]
fn test_str_basic() {
let s: CompactArc<str> = CompactArc::from_str_slice("hello world");
assert_eq!(&*s, "hello world");
assert_eq!(s.len(), 11);
assert!(!s.is_empty());
}
#[test]
fn test_str_empty() {
let s: CompactArc<str> = CompactArc::from_str_slice("");
assert_eq!(&*s, "");
assert_eq!(s.len(), 0);
assert!(s.is_empty());
}
#[test]
fn test_str_clone() {
let s1: CompactArc<str> = CompactArc::from_str_slice("hello");
let s2 = s1.clone();
assert_eq!(&*s1, "hello");
assert_eq!(&*s2, "hello");
assert!(CompactArc::ptr_eq(&s1, &s2));
assert_eq!(CompactArc::strong_count(&s1), 2);
}
#[test]
fn test_str_drop() {
let s1: CompactArc<str> = CompactArc::from_str_slice("test string");
let s2 = s1.clone();
assert_eq!(CompactArc::strong_count(&s1), 2);
drop(s1);
assert_eq!(CompactArc::strong_count(&s2), 1);
assert_eq!(&*s2, "test string");
}
#[test]
fn test_str_unicode() {
let s: CompactArc<str> = CompactArc::from_str_slice("こんにちは世界");
assert_eq!(&*s, "こんにちは世界");
assert_eq!(s.len(), 21);
}
#[test]
fn test_str_from_impls() {
let s1: CompactArc<str> = CompactArc::from("hello");
let s2: CompactArc<str> = CompactArc::from(String::from("world"));
assert_eq!(&*s1, "hello");
assert_eq!(&*s2, "world");
}
#[test]
fn test_str_thin_pointer() {
assert_eq!(std::mem::size_of::<CompactArc<str>>(), 8);
}
#[test]
fn test_str_equality() {
let s1: CompactArc<str> = CompactArc::from_str_slice("hello");
let s2: CompactArc<str> = CompactArc::from_str_slice("hello");
let s3: CompactArc<str> = CompactArc::from_str_slice("world");
assert_eq!(s1, s2);
assert_ne!(s1, s3);
}
#[test]
fn test_str_hash() {
use std::collections::HashMap;
let s: CompactArc<str> = CompactArc::from_str_slice("key");
let mut map = HashMap::new();
map.insert(s.clone(), "value");
assert_eq!(map.get(&s), Some(&"value"));
}
#[test]
fn test_slice_basic() {
let arr: CompactArc<[i32]> = CompactArc::from_slice(&[1, 2, 3, 4, 5]);
assert_eq!(&*arr, &[1, 2, 3, 4, 5]);
assert_eq!(arr.len(), 5);
assert!(!arr.is_empty());
}
#[test]
fn test_slice_empty() {
let empty: &[i32] = &[];
let arr: CompactArc<[i32]> = CompactArc::from_slice(empty);
assert_eq!(&*arr, empty);
assert_eq!(arr.len(), 0);
assert!(arr.is_empty());
}
#[test]
fn test_slice_clone() {
let arr1: CompactArc<[i32]> = CompactArc::from_slice(&[1, 2, 3]);
let arr2 = arr1.clone();
assert_eq!(&*arr1, &[1, 2, 3]);
assert_eq!(&*arr2, &[1, 2, 3]);
assert!(CompactArc::ptr_eq(&arr1, &arr2));
assert_eq!(CompactArc::strong_count(&arr1), 2);
}
#[test]
fn test_slice_thin_pointer() {
assert_eq!(std::mem::size_of::<CompactArc<[i32]>>(), 8);
assert_eq!(std::mem::size_of::<CompactArc<[String]>>(), 8);
}
#[test]
fn test_slice_from_vec() {
let arr: CompactArc<[String]> = CompactArc::from(vec![
String::from("a"),
String::from("b"),
String::from("c"),
]);
assert_eq!(arr.len(), 3);
assert_eq!(&arr[0], "a");
assert_eq!(&arr[1], "b");
assert_eq!(&arr[2], "c");
}
#[test]
fn test_slice_from_compact_vec() {
let mut compact_vec = CompactVec::new();
compact_vec.push(String::from("x"));
compact_vec.push(String::from("y"));
compact_vec.push(String::from("z"));
let arr: CompactArc<[String]> = CompactArc::from_compact_vec(compact_vec);
assert_eq!(arr.len(), 3);
assert_eq!(&arr[0], "x");
assert_eq!(&arr[1], "y");
assert_eq!(&arr[2], "z");
}
#[test]
fn test_from_compact_vec_moves_elements() {
use std::sync::atomic::{AtomicUsize, Ordering};
static DROP_COUNT: AtomicUsize = AtomicUsize::new(0);
static CLONE_COUNT: AtomicUsize = AtomicUsize::new(0);
#[derive(Debug, PartialEq)]
struct MoveTracker(u32);
impl Clone for MoveTracker {
fn clone(&self) -> Self {
CLONE_COUNT.fetch_add(1, Ordering::SeqCst);
MoveTracker(self.0)
}
}
impl Drop for MoveTracker {
fn drop(&mut self) {
DROP_COUNT.fetch_add(1, Ordering::SeqCst);
}
}
DROP_COUNT.store(0, Ordering::SeqCst);
CLONE_COUNT.store(0, Ordering::SeqCst);
{
let mut compact_vec = CompactVec::new();
compact_vec.push(MoveTracker(1));
compact_vec.push(MoveTracker(2));
compact_vec.push(MoveTracker(3));
let arr: CompactArc<[MoveTracker]> = CompactArc::from_compact_vec(compact_vec);
assert_eq!(arr[0].0, 1);
assert_eq!(arr[1].0, 2);
assert_eq!(arr[2].0, 3);
assert_eq!(
CLONE_COUNT.load(Ordering::SeqCst),
0,
"from_compact_vec should move, not clone"
);
}
assert_eq!(DROP_COUNT.load(Ordering::SeqCst), 3);
}
#[test]
fn test_slice_drop_elements() {
use std::sync::atomic::{AtomicUsize, Ordering};
static DROP_COUNT: AtomicUsize = AtomicUsize::new(0);
#[derive(Clone)]
struct DropCounter;
impl Drop for DropCounter {
fn drop(&mut self) {
DROP_COUNT.fetch_add(1, Ordering::SeqCst);
}
}
DROP_COUNT.store(0, Ordering::SeqCst);
{
let arr: CompactArc<[DropCounter]> =
CompactArc::from_slice(&[DropCounter, DropCounter, DropCounter]);
let _arr2 = arr.clone();
}
assert_eq!(DROP_COUNT.load(Ordering::SeqCst), 6);
}
#[test]
fn test_from_slice_panic_safety() {
use std::sync::atomic::{AtomicUsize, Ordering};
static DROP_COUNT: AtomicUsize = AtomicUsize::new(0);
static CLONE_COUNT: AtomicUsize = AtomicUsize::new(0);
#[derive(Debug)]
struct PanicOnThird(u32);
impl Clone for PanicOnThird {
fn clone(&self) -> Self {
let count = CLONE_COUNT.fetch_add(1, Ordering::SeqCst);
if count == 2 {
panic!("Panic on third clone!");
}
PanicOnThird(self.0)
}
}
impl Drop for PanicOnThird {
fn drop(&mut self) {
DROP_COUNT.fetch_add(1, Ordering::SeqCst);
}
}
DROP_COUNT.store(0, Ordering::SeqCst);
CLONE_COUNT.store(0, Ordering::SeqCst);
let slice = &[
PanicOnThird(1),
PanicOnThird(2),
PanicOnThird(3),
PanicOnThird(4),
];
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
let _: CompactArc<[PanicOnThird]> = CompactArc::from_slice(slice);
}));
assert!(result.is_err(), "Should have panicked");
let drops_from_cleanup = DROP_COUNT.load(Ordering::SeqCst);
assert_eq!(
drops_from_cleanup, 2,
"Should have dropped 2 successfully cloned elements"
);
}
#[test]
fn test_from_vec_moves_elements() {
use std::sync::atomic::{AtomicUsize, Ordering};
static DROP_COUNT: AtomicUsize = AtomicUsize::new(0);
static CLONE_COUNT: AtomicUsize = AtomicUsize::new(0);
#[derive(Debug, PartialEq)]
struct MoveTracker(u32);
impl Clone for MoveTracker {
fn clone(&self) -> Self {
CLONE_COUNT.fetch_add(1, Ordering::SeqCst);
MoveTracker(self.0)
}
}
impl Drop for MoveTracker {
fn drop(&mut self) {
DROP_COUNT.fetch_add(1, Ordering::SeqCst);
}
}
DROP_COUNT.store(0, Ordering::SeqCst);
CLONE_COUNT.store(0, Ordering::SeqCst);
{
let vec = vec![MoveTracker(1), MoveTracker(2), MoveTracker(3)];
let arr: CompactArc<[MoveTracker]> = CompactArc::from_vec(vec);
assert_eq!(arr[0].0, 1);
assert_eq!(arr[1].0, 2);
assert_eq!(arr[2].0, 3);
assert_eq!(
CLONE_COUNT.load(Ordering::SeqCst),
0,
"from_vec should move, not clone"
);
}
assert_eq!(DROP_COUNT.load(Ordering::SeqCst), 3);
}
#[test]
fn test_dst_send_sync() {
fn assert_send<T: Send>() {}
fn assert_sync<T: Sync>() {}
assert_send::<CompactArc<str>>();
assert_sync::<CompactArc<str>>();
assert_send::<CompactArc<[i32]>>();
assert_sync::<CompactArc<[i32]>>();
}
#[test]
fn test_str_thread_safety() {
use std::thread;
let s: CompactArc<str> = CompactArc::from_str_slice("shared string");
let mut handles = vec![];
for _ in 0..10 {
let s_clone = s.clone();
handles.push(thread::spawn(move || {
for _ in 0..1000 {
let _ = s_clone.len();
let _another = s_clone.clone();
}
}));
}
for handle in handles {
handle.join().unwrap();
}
drop(s);
}
}