Skip to main content

radixdb_core/
compact_arc.rs

1// Copyright 2026 RadixDB Contributors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! CompactArc - An Arc variant with thin pointers for DSTs
16//!
17//! This module provides `CompactArc<T>`, a thread-safe reference-counted pointer
18//! optimized for dynamically-sized types (DSTs) like `str` and `[T]`.
19//!
20//! ## When to Use CompactArc
21//!
22//! **Use for DSTs** (`str`, `[T]`) when you have many clones sharing one allocation:
23//! - Stack pointer: 8 bytes (thin) vs std::Arc's 16 bytes (fat)
24//! - Heap header: 16 bytes vs std::Arc's 16 bytes
25//! - Net savings: 8 bytes per clone (thin pointer)
26//!
27//! **Avoid for sized types** (`i64`, `String`, structs):
28//! - Stack pointer: 8 bytes (same as std::Arc)
29//! - Heap header: 16 bytes (same as std::Arc)
30//! - No advantage over std::Arc
31//!
32//! ## Memory Layout
33//!
34//! All types use a compact 16-byte header. Type-specific drop logic is resolved
35//! at compile time via monomorphization (no stored function pointer needed):
36//!
37//! ```text
38//! Stack:  [ptr: 8 bytes] ──────────────────┐
39//!                                          ▼
40//! Heap:   [refcount: 8][len: 8][data...]
41//! ```
42//!
43//! ## Pointer Sizes (All Thin!)
44//!
45//! | Type | CompactArc | std::Arc |
46//! |------|------------|----------|
47//! | `CompactArc<i64>` | 8 bytes | 8 bytes |
48//! | `CompactArc<str>` | 8 bytes | 16 bytes |
49//! | `CompactArc<[T]>` | 8 bytes | 16 bytes |
50
51use std::alloc::{alloc, dealloc, handle_alloc_error, Layout};
52use std::borrow::Borrow;
53use std::cmp::Ordering;
54use std::fmt;
55use std::hash::{Hash, Hasher};
56use std::marker::PhantomData;
57use std::mem::{self, ManuallyDrop};
58use std::ops::Deref;
59use std::ptr::{self, NonNull};
60use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering};
61
62use super::CompactVec;
63
64// ============================================================================
65// Unified Header - 16 bytes (no stored function pointer!)
66// ============================================================================
67
68/// Unified header for all CompactArc allocations.
69/// Drop logic is resolved at compile time via the CompactArcDrop trait,
70/// so no function pointer needs to be stored.
71#[repr(C)]
72struct Header {
73    count: AtomicUsize,
74    /// Length of data. For sized types: 0 (or metadata). For str: byte length. For [T]: element count.
75    len: usize,
76    // Data follows immediately after, aligned appropriately
77}
78
79/// Returns the byte offset from the header to the data for type T.
80/// For `CompactArc<[T]>`, pass the element type T, not the slice type.
81#[inline]
82const fn data_offset_for<T>() -> usize {
83    let header_size = mem::size_of::<Header>();
84    let align = mem::align_of::<T>();
85    (header_size + align - 1) & !(align - 1)
86}
87
88// ============================================================================
89// CompactArcDrop - Compile-time drop dispatch (replaces stored fn pointer)
90// ============================================================================
91
92/// Trait for type-specific drop and deallocation logic.
93///
94/// This trait enables compile-time dispatch for dropping CompactArc contents,
95/// replacing the previous runtime function pointer approach. Since Rust
96/// monomorphizes generics, the compiler resolves the correct implementation
97/// at compile time - making this both smaller (no stored pointer) and faster
98/// (direct call instead of indirect).
99///
100/// # Safety
101///
102/// Implementations must correctly drop T's data and deallocate the
103/// header+data allocation using the correct Layout. This trait is
104/// auto-implemented for all types used with CompactArc.
105pub unsafe trait CompactArcDrop {
106    /// Drop the contained data and deallocate the header+data allocation.
107    ///
108    /// # Safety
109    ///
110    /// `ptr` must point to a valid, exclusively-owned CompactArc allocation
111    /// (starting at the Header) that was created by CompactArc's constructors.
112    unsafe fn drop_and_dealloc(ptr: *mut u8);
113}
114
115// SAFETY: Correctly drops a single T at the computed data offset, then deallocates
116// the header+data allocation with the matching Layout.
117unsafe impl<T> CompactArcDrop for T {
118    #[inline]
119    unsafe fn drop_and_dealloc(ptr: *mut u8) {
120        let data_offset = data_offset_for::<T>();
121        let align = mem::align_of::<T>().max(mem::align_of::<Header>());
122
123        // Drop the data
124        let data_ptr = ptr.add(data_offset) as *mut T;
125        ptr::drop_in_place(data_ptr);
126
127        // Deallocate
128        let layout = Layout::from_size_align_unchecked(data_offset + mem::size_of::<T>(), align);
129        dealloc(ptr, layout);
130    }
131}
132
133// SAFETY: str bytes (u8) don't need dropping. Reads len from header to compute
134// the correct deallocation Layout, then deallocates.
135unsafe impl CompactArcDrop for str {
136    #[inline]
137    unsafe fn drop_and_dealloc(ptr: *mut u8) {
138        let header = ptr as *mut Header;
139        let len = (*header).len;
140        let data_offset = data_offset_for::<u8>(); // str has align 1
141        let total_size = data_offset + len;
142        let layout = Layout::from_size_align_unchecked(total_size, mem::align_of::<Header>());
143        dealloc(ptr, layout);
144    }
145}
146
147#[allow(clippy::manual_slice_size_calculation)]
148// SAFETY: Reads len from header to reconstruct the slice, drops all len elements
149// via drop_in_place, then deallocates the header+data allocation with the matching Layout.
150unsafe impl<T> CompactArcDrop for [T] {
151    #[inline]
152    unsafe fn drop_and_dealloc(ptr: *mut u8) {
153        let header = ptr as *mut Header;
154        let len = (*header).len;
155        let data_offset = data_offset_for::<T>();
156        let align = mem::align_of::<T>().max(mem::align_of::<Header>());
157
158        // Drop elements
159        let data_ptr = ptr.add(data_offset) as *mut T;
160        ptr::drop_in_place(std::ptr::slice_from_raw_parts_mut(data_ptr, len));
161
162        // Deallocate
163        let layout =
164            Layout::from_size_align_unchecked(data_offset + mem::size_of::<T>() * len, align);
165        dealloc(ptr, layout);
166    }
167}
168
169// ============================================================================
170// CompactArc - Unified type with thin pointers!
171// ============================================================================
172
173/// A thread-safe reference-counted pointer without weak reference support.
174///
175/// `CompactArc<T>` provides shared ownership of a value of type `T`, allocated
176/// on the heap. It saves memory compared to `std::sync::Arc`:
177/// - 8 bytes less per allocation (no weak count)
178/// - Thin pointers for DSTs (8 bytes instead of 16 for `str` and `[T]`)
179///
180/// # Pointer Sizes
181///
182/// | Type | Size |
183/// |------|------|
184/// | `CompactArc<i64>` | 8 bytes |
185/// | `CompactArc<str>` | 8 bytes (thin!) |
186/// | `CompactArc<[T]>` | 8 bytes (thin!) |
187pub struct CompactArc<T: ?Sized + CompactArcDrop> {
188    /// Thin pointer to Header (always 8 bytes, even for DSTs!)
189    ptr: NonNull<Header>,
190    _marker: PhantomData<T>,
191}
192
193// SAFETY: CompactArc can be sent between threads if T can be sent and shared.
194// The refcount is atomic (AtomicUsize) ensuring thread-safe increment/decrement.
195// T: Send + Sync ensures the data itself can be safely shared across threads.
196unsafe impl<T: ?Sized + CompactArcDrop + Send + Sync> Send for CompactArc<T> {}
197// SAFETY: Same reasoning as Send - atomic refcount and T: Send + Sync.
198unsafe impl<T: ?Sized + CompactArcDrop + Send + Sync> Sync for CompactArc<T> {}
199
200// ============================================================================
201// THE SINGLE DROP IMPL - Compile-time dispatch via CompactArcDrop!
202// ============================================================================
203
204impl<T: ?Sized + CompactArcDrop> Drop for CompactArc<T> {
205    #[inline]
206    fn drop(&mut self) {
207        let header = self.ptr.as_ptr();
208        // SAFETY: self.ptr is always valid (NonNull) and points to a properly initialized
209        // Header. The atomic decrement is safe for concurrent access. Release ordering
210        // ensures our writes are visible to whoever sees the decremented count.
211        let old_count = unsafe { (*header).count.fetch_sub(1, AtomicOrdering::Release) };
212
213        if old_count == 1 {
214            // An acquire load observes the release sequence of reference decrements.
215            // Unlike an acquire fence, it is also understood by ThreadSanitizer.
216            // SAFETY: the allocation remains alive until drop_and_dealloc below.
217            unsafe { (*header).count.load(AtomicOrdering::Acquire) };
218            // SAFETY: old_count == 1 means we had the last reference. The Acquire load
219            // synchronizes with Release in other drops, ensuring we see all their writes.
220            // T::drop_and_dealloc is resolved at compile time via monomorphization.
221            unsafe {
222                T::drop_and_dealloc(header as *mut u8);
223            }
224        }
225    }
226}
227
228// ============================================================================
229// THE SINGLE CLONE IMPL - Works for ALL types!
230// ============================================================================
231
232impl<T: ?Sized + CompactArcDrop> Clone for CompactArc<T> {
233    #[inline]
234    fn clone(&self) -> Self {
235        let header = self.ptr.as_ptr();
236        // SAFETY: self.ptr is always valid (NonNull) and points to a properly initialized
237        // Header. The atomic increment is safe for concurrent access. Relaxed ordering
238        // is sufficient since we don't need to synchronize any data with this operation.
239        let old_count = unsafe { (*header).count.fetch_add(1, AtomicOrdering::Relaxed) };
240
241        if old_count > isize::MAX as usize {
242            std::process::abort();
243        }
244
245        CompactArc {
246            ptr: self.ptr,
247            _marker: PhantomData,
248        }
249    }
250}
251
252// ============================================================================
253// Common methods
254// ============================================================================
255
256impl<T: ?Sized + CompactArcDrop> CompactArc<T> {
257    /// Returns `true` if the two `CompactArc`s point to the same allocation.
258    #[inline]
259    pub fn ptr_eq(this: &Self, other: &Self) -> bool {
260        ptr::addr_eq(this.ptr.as_ptr(), other.ptr.as_ptr())
261    }
262
263    /// Returns the number of strong references to this allocation.
264    ///
265    /// Note: This uses `Relaxed` ordering and should only be used for
266    /// debugging/logging purposes, not for synchronization decisions.
267    #[inline]
268    pub fn strong_count(this: &Self) -> usize {
269        // SAFETY: this.ptr is always valid (NonNull) and points to a properly initialized
270        // Header. Reading the atomic count with Relaxed ordering is always safe.
271        unsafe { (*this.ptr.as_ptr()).count.load(AtomicOrdering::Relaxed) }
272    }
273
274    /// Returns `true` if this is the only reference to the allocation.
275    ///
276    /// Uses `Acquire` ordering to synchronize with `Release` in `drop`,
277    /// ensuring visibility of all modifications made by other threads
278    /// before they dropped their references.
279    #[inline]
280    fn is_unique(this: &Self) -> bool {
281        // SAFETY: this.ptr is always valid (NonNull) and points to a properly initialized
282        // Header. Acquire ordering synchronizes with Release in drop.
283        unsafe { (*this.ptr.as_ptr()).count.load(AtomicOrdering::Acquire) == 1 }
284    }
285
286    /// Returns the metadata stored in the header (used for count).
287    #[inline]
288    pub fn meta(this: &Self) -> usize {
289        // SAFETY: this.ptr is always valid (NonNull) and points to a properly initialized
290        // Header. The len field is immutable after construction (for DSTs) or can be
291        // safely read (for sized types using it as metadata).
292        unsafe { (*this.ptr.as_ptr()).len }
293    }
294}
295
296// ============================================================================
297// Sized type implementations
298// ============================================================================
299
300impl<T: CompactArcDrop> CompactArc<T> {
301    /// Creates a new `CompactArc<T>` containing the given value.
302    #[inline]
303    #[must_use]
304    pub fn new(data: T) -> Self {
305        Self::new_with_meta(data, 0)
306    }
307
308    /// Creates a new `CompactArc<T>` containing the given value and metadata.
309    /// The metadata is stored in the header's `len` field, which is unused for Sized types.
310    #[inline]
311    #[must_use]
312    pub fn new_with_meta(data: T, meta: usize) -> Self {
313        let data_offset = data_offset_for::<T>();
314        let align = mem::align_of::<T>().max(mem::align_of::<Header>());
315        let total_size = data_offset + mem::size_of::<T>();
316        let layout = Layout::from_size_align(total_size, align).expect("layout overflow");
317
318        // SAFETY: We allocate memory with the correct layout for Header + T.
319        // We initialize all fields before returning. The allocation is guaranteed
320        // to be non-null (we call handle_alloc_error on failure). data_offset_for<T>()
321        // ensures proper alignment for T after the header.
322        unsafe {
323            let ptr = alloc(layout);
324            if ptr.is_null() {
325                handle_alloc_error(layout);
326            }
327
328            // Write header
329            let header = ptr as *mut Header;
330            ptr::write(
331                header,
332                Header {
333                    count: AtomicUsize::new(1),
334                    len: meta,
335                },
336            );
337
338            // Write data
339            let data_ptr = ptr.add(data_offset) as *mut T;
340            ptr::write(data_ptr, data);
341
342            CompactArc {
343                ptr: NonNull::new_unchecked(header),
344                _marker: PhantomData,
345            }
346        }
347    }
348
349    /// Attempts to unwrap the `CompactArc`, returning the inner value if this
350    /// is the only reference.
351    #[inline]
352    pub fn try_unwrap(this: Self) -> Result<T, Self> {
353        let header = this.ptr.as_ptr();
354
355        // SAFETY: this.ptr is valid. compare_exchange atomically checks if count == 1
356        // and sets it to 0. Acquire ordering on success synchronizes with Release in
357        // other drops, ensuring we see all their writes.
358        if unsafe {
359            (*header)
360                .count
361                .compare_exchange(1, 0, AtomicOrdering::Acquire, AtomicOrdering::Relaxed)
362                .is_ok()
363        } {
364            let _ = ManuallyDrop::new(this);
365
366            // SAFETY: compare_exchange succeeded, so we had the only reference and now
367            // own the data exclusively. We read the data out (moving it), then deallocate
368            // the memory without calling the dropper (since we took ownership of the data).
369            unsafe {
370                // Read data
371                let data_offset = data_offset_for::<T>();
372                let data_ptr = (header as *const u8).add(data_offset) as *const T;
373                let data = ptr::read(data_ptr);
374
375                // Deallocate (without dropping since we took the data)
376                let align = mem::align_of::<T>().max(mem::align_of::<Header>());
377                let layout =
378                    Layout::from_size_align_unchecked(data_offset + mem::size_of::<T>(), align);
379                dealloc(header as *mut u8, layout);
380
381                Ok(data)
382            }
383        } else {
384            Err(this)
385        }
386    }
387
388    /// Gets a mutable reference to the inner value, if there are no other references.
389    ///
390    /// Uses `Acquire` ordering to synchronize with other threads that may have
391    /// dropped their references, ensuring all their modifications are visible.
392    #[inline]
393    pub fn get_mut(this: &mut Self) -> Option<&mut T> {
394        if Self::is_unique(this) {
395            // SAFETY: is_unique() returned true with Acquire ordering, meaning we have
396            // exclusive access. this.ptr is valid and data_offset_for<T>() gives the
397            // correct offset to the properly aligned T.
398            unsafe {
399                let data_ptr = (this.ptr.as_ptr() as *mut u8).add(data_offset_for::<T>()) as *mut T;
400                Some(&mut *data_ptr)
401            }
402        } else {
403            None
404        }
405    }
406
407    /// Makes a mutable reference to the inner value (clone-on-write).
408    ///
409    /// If there are other references, clones the data into a new allocation.
410    #[inline]
411    pub fn make_mut(this: &mut Self) -> &mut T
412    where
413        T: Clone,
414    {
415        // Check if we're the only reference (uses Acquire ordering)
416        if !Self::is_unique(this) {
417            let meta = Self::meta(this);
418            // Clone the data since there are other references
419            *this = CompactArc::new_with_meta((**this).clone(), meta);
420        }
421        // SAFETY: After the above, we're guaranteed to be the only reference
422        Self::get_mut(this).unwrap()
423    }
424
425    /// Returns a raw pointer to the contained data.
426    #[inline]
427    pub fn as_ptr(this: &Self) -> *const T {
428        // Derive from the header pointer via raw pointer arithmetic to avoid
429        // creating a shared reference (&T) that would restrict the borrow stack.
430        // Going through Deref (&*this) creates a SharedReadOnly tag that
431        // invalidates later writes to the header (e.g., refcount decrement).
432        let header = this.ptr.as_ptr();
433        unsafe { (header as *const u8).add(data_offset_for::<T>()) as *const T }
434    }
435
436    /// Converts a `CompactArc<T>` into a raw pointer.
437    #[inline]
438    pub fn into_raw(this: Self) -> *const T {
439        // Use raw pointer arithmetic instead of &*this to avoid Stacked Borrows
440        // violation: a SharedReadOnly retag from &*this would conflict with
441        // the SharedReadWrite needed by Drop to decrement the refcount.
442        let header = this.ptr.as_ptr();
443        let ptr = unsafe { (header as *const u8).add(data_offset_for::<T>()) as *const T };
444        mem::forget(this);
445        ptr
446    }
447
448    /// Constructs a `CompactArc<T>` from a raw pointer.
449    ///
450    /// # Safety
451    ///
452    /// The raw pointer must have been previously returned by `CompactArc::into_raw`.
453    #[inline]
454    pub unsafe fn from_raw(ptr: *const T) -> Self {
455        let header = (ptr as *const u8).sub(data_offset_for::<T>()) as *mut Header;
456        CompactArc {
457            ptr: NonNull::new_unchecked(header),
458            _marker: PhantomData,
459        }
460    }
461}
462
463impl<T: CompactArcDrop> Deref for CompactArc<T> {
464    type Target = T;
465
466    #[inline]
467    fn deref(&self) -> &T {
468        // SAFETY: self.ptr is always valid (NonNull) and points to a properly initialized
469        // allocation. data_offset_for<T>() gives the correct offset to the properly aligned
470        // T data. The data was initialized in new() or new_with_meta().
471        unsafe {
472            let data_ptr = (self.ptr.as_ptr() as *const u8).add(data_offset_for::<T>()) as *const T;
473            &*data_ptr
474        }
475    }
476}
477
478impl<T: CompactArcDrop + Default> Default for CompactArc<T> {
479    #[inline]
480    fn default() -> Self {
481        CompactArc::new(T::default())
482    }
483}
484
485impl<T: CompactArcDrop + fmt::Debug> fmt::Debug for CompactArc<T> {
486    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
487        fmt::Debug::fmt(&**self, f)
488    }
489}
490
491impl<T: CompactArcDrop + fmt::Display> fmt::Display for CompactArc<T> {
492    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
493        fmt::Display::fmt(&**self, f)
494    }
495}
496
497impl<T: CompactArcDrop + PartialEq> PartialEq for CompactArc<T> {
498    #[inline]
499    fn eq(&self, other: &Self) -> bool {
500        if CompactArc::ptr_eq(self, other) {
501            return true;
502        }
503        **self == **other
504    }
505}
506
507impl<T: CompactArcDrop + Eq> Eq for CompactArc<T> {}
508
509impl<T: CompactArcDrop + PartialOrd> PartialOrd for CompactArc<T> {
510    #[inline]
511    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
512        (**self).partial_cmp(&**other)
513    }
514}
515
516impl<T: CompactArcDrop + Ord> Ord for CompactArc<T> {
517    #[inline]
518    fn cmp(&self, other: &Self) -> Ordering {
519        (**self).cmp(&**other)
520    }
521}
522
523impl<T: CompactArcDrop + Hash> Hash for CompactArc<T> {
524    fn hash<H: Hasher>(&self, state: &mut H) {
525        (**self).hash(state)
526    }
527}
528
529impl<T: CompactArcDrop> Borrow<T> for CompactArc<T> {
530    fn borrow(&self) -> &T {
531        self
532    }
533}
534
535impl<T: CompactArcDrop> AsRef<T> for CompactArc<T> {
536    fn as_ref(&self) -> &T {
537        self
538    }
539}
540
541impl<T: CompactArcDrop> From<T> for CompactArc<T> {
542    #[inline]
543    fn from(value: T) -> Self {
544        CompactArc::new(value)
545    }
546}
547
548// ============================================================================
549// DST Support: str (Thin Pointer!)
550// ============================================================================
551
552impl CompactArc<str> {
553    /// Creates a new `CompactArc<str>` from a string slice.
554    ///
555    /// The pointer is only 8 bytes (thin), with length stored in heap header.
556    #[must_use]
557    pub fn from_str_slice(s: &str) -> Self {
558        let len = s.len();
559        let data_offset = data_offset_for::<u8>(); // str has align 1
560        let total_size = data_offset + len;
561        let layout = Layout::from_size_align(total_size, mem::align_of::<Header>())
562            .expect("layout overflow");
563
564        // SAFETY: We allocate memory with the correct layout for Header + str bytes.
565        // We initialize all fields before returning. The source string s is valid UTF-8,
566        // and we copy its bytes verbatim, preserving UTF-8 validity.
567        unsafe {
568            let ptr = alloc(layout);
569            if ptr.is_null() {
570                handle_alloc_error(layout);
571            }
572
573            // Write header
574            let header = ptr as *mut Header;
575            ptr::write(
576                header,
577                Header {
578                    count: AtomicUsize::new(1),
579                    len,
580                },
581            );
582
583            // Write string bytes
584            let data_ptr = ptr.add(data_offset);
585            ptr::copy_nonoverlapping(s.as_ptr(), data_ptr, len);
586
587            CompactArc {
588                ptr: NonNull::new_unchecked(header),
589                _marker: PhantomData,
590            }
591        }
592    }
593
594    /// Returns the length of the string in bytes.
595    #[inline]
596    pub fn len(&self) -> usize {
597        // SAFETY: self.ptr is valid and points to an initialized Header.
598        // The len field contains the string length set during construction.
599        unsafe { (*self.ptr.as_ptr()).len }
600    }
601
602    /// Returns true if the string is empty.
603    #[inline]
604    pub fn is_empty(&self) -> bool {
605        self.len() == 0
606    }
607}
608
609impl Deref for CompactArc<str> {
610    type Target = str;
611
612    #[inline]
613    fn deref(&self) -> &str {
614        // SAFETY: self.ptr is valid and points to an initialized allocation.
615        // The len field contains the correct string length. The bytes were copied
616        // from a valid UTF-8 string in from_str_slice(), so they are valid UTF-8.
617        unsafe {
618            let header = self.ptr.as_ptr();
619            let len = (*header).len;
620            let data_offset = data_offset_for::<u8>(); // str has align 1
621            let data_ptr = (header as *const u8).add(data_offset);
622            std::str::from_utf8_unchecked(std::slice::from_raw_parts(data_ptr, len))
623        }
624    }
625}
626
627impl From<&str> for CompactArc<str> {
628    #[inline]
629    fn from(s: &str) -> Self {
630        CompactArc::from_str_slice(s)
631    }
632}
633
634impl From<String> for CompactArc<str> {
635    #[inline]
636    fn from(s: String) -> Self {
637        CompactArc::from_str_slice(&s)
638    }
639}
640
641impl fmt::Debug for CompactArc<str> {
642    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
643        fmt::Debug::fmt(&**self, f)
644    }
645}
646
647impl fmt::Display for CompactArc<str> {
648    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
649        fmt::Display::fmt(&**self, f)
650    }
651}
652
653impl PartialEq for CompactArc<str> {
654    #[inline]
655    fn eq(&self, other: &Self) -> bool {
656        if CompactArc::ptr_eq(self, other) {
657            return true;
658        }
659        **self == **other
660    }
661}
662
663impl Eq for CompactArc<str> {}
664
665impl PartialOrd for CompactArc<str> {
666    #[inline]
667    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
668        Some(self.cmp(other))
669    }
670}
671
672impl Ord for CompactArc<str> {
673    #[inline]
674    fn cmp(&self, other: &Self) -> Ordering {
675        (**self).cmp(&**other)
676    }
677}
678
679impl Hash for CompactArc<str> {
680    fn hash<H: Hasher>(&self, state: &mut H) {
681        (**self).hash(state)
682    }
683}
684
685impl Borrow<str> for CompactArc<str> {
686    fn borrow(&self) -> &str {
687        self
688    }
689}
690
691impl AsRef<str> for CompactArc<str> {
692    fn as_ref(&self) -> &str {
693        self
694    }
695}
696
697// ============================================================================
698// DST Support: [T] (Thin Pointer!)
699// ============================================================================
700
701impl<T> CompactArc<[T]> {
702    /// Creates a new `CompactArc<[T]>` by moving elements from a Vec.
703    ///
704    /// This is more efficient than `from_slice` as it moves elements instead of cloning.
705    #[must_use]
706    pub fn from_vec(mut vec: Vec<T>) -> Self {
707        let len = vec.len();
708        let data_offset = data_offset_for::<T>();
709        let align = mem::align_of::<T>().max(mem::align_of::<Header>());
710        let data_size = mem::size_of::<T>() * len;
711        let layout =
712            Layout::from_size_align(data_offset + data_size, align).expect("layout overflow");
713
714        // SAFETY: We allocate memory with the correct layout for Header + [T].
715        // We copy (move) the elements from vec into the allocation, then set vec's len to 0
716        // to prevent double-free. The vec's buffer is still freed when vec drops, but
717        // its elements have been moved to our allocation.
718        unsafe {
719            let ptr = alloc(layout);
720            if ptr.is_null() {
721                handle_alloc_error(layout);
722            }
723
724            // Write header
725            let header = ptr as *mut Header;
726            ptr::write(
727                header,
728                Header {
729                    count: AtomicUsize::new(1),
730                    len,
731                },
732            );
733
734            // Move elements from Vec (copy bytes, then prevent Vec from dropping them)
735            let data_ptr = ptr.add(data_offset) as *mut T;
736            ptr::copy_nonoverlapping(vec.as_ptr(), data_ptr, len);
737
738            // Prevent Vec from dropping the moved elements (buffer will still be freed)
739            vec.set_len(0);
740
741            CompactArc {
742                ptr: NonNull::new_unchecked(header),
743                _marker: PhantomData,
744            }
745        }
746    }
747
748    /// Creates a new `CompactArc<[T]>` by moving elements from a CompactVec.
749    ///
750    /// This is more efficient than `from_slice` as it moves elements instead of cloning.
751    /// Avoids the intermediate Vec conversion compared to `from_vec`.
752    #[must_use]
753    pub fn from_compact_vec(mut vec: CompactVec<T>) -> Self {
754        let len = vec.len();
755        let data_offset = data_offset_for::<T>();
756        let align = mem::align_of::<T>().max(mem::align_of::<Header>());
757        let data_size = mem::size_of::<T>() * len;
758        let layout =
759            Layout::from_size_align(data_offset + data_size, align).expect("layout overflow");
760
761        // SAFETY: We allocate memory with the correct layout for Header + [T].
762        // We copy (move) the elements from vec into the allocation, then set vec's len to 0
763        // to prevent double-free. The vec's buffer is still freed when vec drops, but
764        // its elements have been moved to our allocation.
765        unsafe {
766            let ptr = alloc(layout);
767            if ptr.is_null() {
768                handle_alloc_error(layout);
769            }
770
771            // Write header
772            let header = ptr as *mut Header;
773            ptr::write(
774                header,
775                Header {
776                    count: AtomicUsize::new(1),
777                    len,
778                },
779            );
780
781            // Move elements from CompactVec (copy bytes, then prevent CompactVec from dropping them)
782            let data_ptr = ptr.add(data_offset) as *mut T;
783            ptr::copy_nonoverlapping(vec.as_ptr(), data_ptr, len);
784
785            // Prevent CompactVec from dropping the moved elements (buffer will still be freed)
786            vec.set_len(0);
787
788            CompactArc {
789                ptr: NonNull::new_unchecked(header),
790                _marker: PhantomData,
791            }
792        }
793    }
794}
795
796impl<T: Clone> CompactArc<[T]> {
797    /// Creates a new `CompactArc<[T]>` from a slice by cloning elements.
798    ///
799    /// The pointer is only 8 bytes (thin), with length stored in heap header.
800    ///
801    /// # Panic Safety
802    ///
803    /// If `T::clone()` panics, all successfully cloned elements are dropped
804    /// and the allocation is freed. No memory is leaked.
805    #[must_use]
806    pub fn from_slice(slice: &[T]) -> Self {
807        let len = slice.len();
808        let data_offset = data_offset_for::<T>();
809        let align = mem::align_of::<T>().max(mem::align_of::<Header>());
810        let layout = Layout::from_size_align(data_offset + mem::size_of_val(slice), align)
811            .expect("layout overflow");
812
813        // SAFETY: We allocate memory with the correct layout for Header + [T].
814        // We use a CloneGuard for panic safety - if any clone() panics, the guard
815        // drops all successfully cloned elements and frees the allocation.
816        // On success, we forget the guard and return the initialized CompactArc.
817        unsafe {
818            let ptr = alloc(layout);
819            if ptr.is_null() {
820                handle_alloc_error(layout);
821            }
822
823            // Write header
824            let header = ptr as *mut Header;
825            ptr::write(
826                header,
827                Header {
828                    count: AtomicUsize::new(1),
829                    len,
830                },
831            );
832
833            let data_ptr = ptr.add(data_offset) as *mut T;
834
835            // RAII guard for panic safety: if clone() panics, this cleans up
836            struct CloneGuard<T> {
837                data_ptr: *mut T,
838                alloc_ptr: *mut u8,
839                layout: Layout,
840                written: usize,
841            }
842
843            impl<T> Drop for CloneGuard<T> {
844                fn drop(&mut self) {
845                    // SAFETY: data_ptr points to an array where the first `written` elements
846                    // are initialized. We drop those elements, then deallocate the memory
847                    // using the stored layout. This is only called on panic during clone.
848                    unsafe {
849                        // Drop all successfully written elements
850                        let slice = ptr::slice_from_raw_parts_mut(self.data_ptr, self.written);
851                        ptr::drop_in_place(slice);
852                        // Deallocate the memory
853                        dealloc(self.alloc_ptr, self.layout);
854                    }
855                }
856            }
857
858            let mut guard = CloneGuard {
859                data_ptr,
860                alloc_ptr: ptr,
861                layout,
862                written: 0,
863            };
864
865            // Clone elements - if this panics, guard cleans up
866            for (i, item) in slice.iter().enumerate() {
867                ptr::write(data_ptr.add(i), item.clone());
868                guard.written += 1;
869            }
870
871            // Success! Prevent guard from cleaning up
872            mem::forget(guard);
873
874            CompactArc {
875                ptr: NonNull::new_unchecked(header),
876                _marker: PhantomData,
877            }
878        }
879    }
880}
881
882impl<T> CompactArc<[T]> {
883    /// Returns the number of elements in the slice.
884    #[inline]
885    pub fn len(&self) -> usize {
886        // SAFETY: self.ptr is valid and points to an initialized Header.
887        // The len field contains the slice length set during construction.
888        unsafe { (*self.ptr.as_ptr()).len }
889    }
890
891    /// Returns true if the slice is empty.
892    #[inline]
893    pub fn is_empty(&self) -> bool {
894        self.len() == 0
895    }
896
897    /// Returns a raw mutable pointer to the first element of the data region.
898    ///
899    /// Derives the pointer from the header via raw pointer arithmetic, bypassing
900    /// `Deref` (which would create a SharedReadOnly borrow tag under Stacked
901    /// Borrows and prevent subsequent mutable access to the same allocation).
902    #[inline]
903    #[doc(hidden)]
904    pub fn data_ptr_mut(&self) -> *mut T {
905        unsafe { (self.ptr.as_ptr() as *mut u8).add(data_offset_for::<T>()) as *mut T }
906    }
907}
908
909impl<T> Deref for CompactArc<[T]> {
910    type Target = [T];
911
912    #[inline]
913    fn deref(&self) -> &[T] {
914        // SAFETY: self.ptr is valid and points to an initialized allocation.
915        // The len field contains the correct element count. data_offset_for<T>()
916        // gives the correct offset to the properly aligned [T] data. All len
917        // elements were initialized in from_slice() or from_vec().
918        unsafe {
919            let header = self.ptr.as_ptr();
920            let len = (*header).len;
921            let data_ptr = (header as *const u8).add(data_offset_for::<T>()) as *const T;
922            std::slice::from_raw_parts(data_ptr, len)
923        }
924    }
925}
926
927impl<T: Clone> From<&[T]> for CompactArc<[T]> {
928    #[inline]
929    fn from(slice: &[T]) -> Self {
930        CompactArc::from_slice(slice)
931    }
932}
933
934impl<T> From<Vec<T>> for CompactArc<[T]> {
935    #[inline]
936    fn from(vec: Vec<T>) -> Self {
937        CompactArc::from_vec(vec)
938    }
939}
940
941impl<T: fmt::Debug> fmt::Debug for CompactArc<[T]> {
942    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
943        fmt::Debug::fmt(&**self, f)
944    }
945}
946
947impl<T: PartialEq> PartialEq for CompactArc<[T]> {
948    #[inline]
949    fn eq(&self, other: &Self) -> bool {
950        if CompactArc::ptr_eq(self, other) {
951            return true;
952        }
953        **self == **other
954    }
955}
956
957impl<T: Eq> Eq for CompactArc<[T]> {}
958
959impl<T: PartialOrd> PartialOrd for CompactArc<[T]> {
960    #[inline]
961    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
962        (**self).partial_cmp(&**other)
963    }
964}
965
966impl<T: Ord> Ord for CompactArc<[T]> {
967    #[inline]
968    fn cmp(&self, other: &Self) -> Ordering {
969        (**self).cmp(&**other)
970    }
971}
972
973impl<T: Hash> Hash for CompactArc<[T]> {
974    fn hash<H: Hasher>(&self, state: &mut H) {
975        (**self).hash(state)
976    }
977}
978
979impl<T> Borrow<[T]> for CompactArc<[T]> {
980    fn borrow(&self) -> &[T] {
981        self
982    }
983}
984
985impl<T> AsRef<[T]> for CompactArc<[T]> {
986    fn as_ref(&self) -> &[T] {
987        self
988    }
989}
990
991#[cfg(test)]
992mod tests {
993    use super::*;
994
995    #[test]
996    fn test_new_and_deref() {
997        let arc = CompactArc::new(42);
998        assert_eq!(*arc, 42);
999    }
1000
1001    #[test]
1002    fn test_clone_and_count() {
1003        let arc = CompactArc::new(42);
1004        assert_eq!(CompactArc::strong_count(&arc), 1);
1005
1006        let arc2 = arc.clone();
1007        assert_eq!(CompactArc::strong_count(&arc), 2);
1008        assert_eq!(CompactArc::strong_count(&arc2), 2);
1009
1010        drop(arc2);
1011        assert_eq!(CompactArc::strong_count(&arc), 1);
1012    }
1013
1014    #[test]
1015    fn test_ptr_eq() {
1016        let arc1 = CompactArc::new(42);
1017        let arc2 = arc1.clone();
1018        let arc3 = CompactArc::new(42);
1019
1020        assert!(CompactArc::ptr_eq(&arc1, &arc2));
1021        assert!(!CompactArc::ptr_eq(&arc1, &arc3));
1022    }
1023
1024    #[test]
1025    fn test_try_unwrap_success() {
1026        let arc = CompactArc::new(42);
1027        let value = CompactArc::try_unwrap(arc).unwrap();
1028        assert_eq!(value, 42);
1029    }
1030
1031    #[test]
1032    fn test_try_unwrap_failure() {
1033        let arc = CompactArc::new(42);
1034        let _arc2 = arc.clone();
1035        let result = CompactArc::try_unwrap(arc);
1036        assert!(result.is_err());
1037    }
1038
1039    #[test]
1040    fn test_get_mut() {
1041        let mut arc = CompactArc::new(42);
1042        *CompactArc::get_mut(&mut arc).unwrap() = 100;
1043        assert_eq!(*arc, 100);
1044
1045        let _arc2 = arc.clone();
1046        assert!(CompactArc::get_mut(&mut arc).is_none());
1047    }
1048
1049    #[test]
1050    fn test_make_mut() {
1051        let mut arc = CompactArc::new(42);
1052        *CompactArc::make_mut(&mut arc) = 100;
1053        assert_eq!(*arc, 100);
1054
1055        let arc2 = arc.clone();
1056        *CompactArc::make_mut(&mut arc) = 200;
1057        assert_eq!(*arc, 200);
1058        assert_eq!(*arc2, 100);
1059    }
1060
1061    #[test]
1062    fn test_into_raw_from_raw() {
1063        let arc = CompactArc::new(42);
1064        let ptr = CompactArc::into_raw(arc);
1065
1066        // SAFETY: ptr was obtained from into_raw and has not been used since
1067        let arc2 = unsafe { CompactArc::from_raw(ptr) };
1068        assert_eq!(*arc2, 42);
1069    }
1070
1071    #[test]
1072    fn test_debug_display() {
1073        let arc = CompactArc::new(42);
1074        assert_eq!(format!("{:?}", arc), "42");
1075        assert_eq!(format!("{}", arc), "42");
1076    }
1077
1078    #[test]
1079    fn test_equality() {
1080        let arc1 = CompactArc::new(42);
1081        let arc2 = CompactArc::new(42);
1082        let arc3 = CompactArc::new(100);
1083
1084        assert_eq!(arc1, arc2);
1085        assert_ne!(arc1, arc3);
1086    }
1087
1088    #[test]
1089    fn test_ordering() {
1090        let arc1 = CompactArc::new(1);
1091        let arc2 = CompactArc::new(2);
1092
1093        assert!(arc1 < arc2);
1094        assert!(arc2 > arc1);
1095    }
1096
1097    #[test]
1098    fn test_hash() {
1099        use std::collections::HashMap;
1100
1101        let arc = CompactArc::new(42);
1102        let mut map = HashMap::new();
1103        map.insert(arc.clone(), "value");
1104
1105        assert_eq!(map.get(&arc), Some(&"value"));
1106    }
1107
1108    #[test]
1109    fn test_send_sync() {
1110        fn assert_send<T: Send>() {}
1111        fn assert_sync<T: Sync>() {}
1112
1113        assert_send::<CompactArc<i32>>();
1114        assert_sync::<CompactArc<i32>>();
1115    }
1116
1117    #[test]
1118    fn test_sized_pointer_size() {
1119        // CompactArc<T> should be 8 bytes (thin pointer)
1120        assert_eq!(std::mem::size_of::<CompactArc<i32>>(), 8);
1121        assert_eq!(std::mem::size_of::<CompactArc<i64>>(), 8);
1122        assert_eq!(std::mem::size_of::<CompactArc<String>>(), 8);
1123    }
1124
1125    #[test]
1126    fn test_header_size() {
1127        // Header should be exactly 16 bytes (refcount + len, no dropper)
1128        assert_eq!(std::mem::size_of::<Header>(), 16);
1129    }
1130
1131    #[test]
1132    fn test_high_alignment_type() {
1133        #[repr(align(64))]
1134        #[derive(Debug, Clone, PartialEq)]
1135        struct Aligned64 {
1136            value: u64,
1137        }
1138
1139        let arc = CompactArc::new(Aligned64 { value: 42 });
1140        assert_eq!(arc.value, 42);
1141
1142        // Verify data pointer is properly aligned
1143        let data_ptr = CompactArc::as_ptr(&arc);
1144        assert_eq!(data_ptr as usize % 64, 0, "Data should be 64-byte aligned");
1145
1146        // Test clone and drop
1147        let arc2 = arc.clone();
1148        assert_eq!(arc2.value, 42);
1149        assert_eq!(CompactArc::strong_count(&arc), 2);
1150
1151        drop(arc);
1152        assert_eq!(arc2.value, 42);
1153
1154        // Test try_unwrap
1155        let value = CompactArc::try_unwrap(arc2).unwrap();
1156        assert_eq!(value.value, 42);
1157    }
1158
1159    #[test]
1160    fn test_high_alignment_slice() {
1161        #[repr(align(32))]
1162        #[derive(Debug, Clone, PartialEq)]
1163        struct Aligned32(u32);
1164
1165        let arr: CompactArc<[Aligned32]> =
1166            CompactArc::from_slice(&[Aligned32(1), Aligned32(2), Aligned32(3)]);
1167
1168        assert_eq!(arr.len(), 3);
1169        assert_eq!(arr[0], Aligned32(1));
1170        assert_eq!(arr[1], Aligned32(2));
1171        assert_eq!(arr[2], Aligned32(3));
1172
1173        // Verify first element is properly aligned
1174        let first_ptr = &arr[0] as *const Aligned32;
1175        assert_eq!(
1176            first_ptr as usize % 32,
1177            0,
1178            "Elements should be 32-byte aligned"
1179        );
1180    }
1181
1182    #[test]
1183    fn test_drop_complex_type() {
1184        use std::sync::atomic::{AtomicUsize, Ordering};
1185
1186        static DROP_COUNT: AtomicUsize = AtomicUsize::new(0);
1187
1188        struct DropCounter;
1189        impl Drop for DropCounter {
1190            fn drop(&mut self) {
1191                DROP_COUNT.fetch_add(1, Ordering::SeqCst);
1192            }
1193        }
1194
1195        DROP_COUNT.store(0, Ordering::SeqCst);
1196
1197        {
1198            let arc = CompactArc::new(DropCounter);
1199            let _arc2 = arc.clone();
1200            let _arc3 = arc.clone();
1201        }
1202
1203        assert_eq!(DROP_COUNT.load(Ordering::SeqCst), 1);
1204    }
1205
1206    #[test]
1207    fn test_last_reference_drops_before_workers_are_joined() {
1208        use std::sync::{Arc, Barrier};
1209
1210        let barrier = Arc::new(Barrier::new(8));
1211        let value = CompactArc::new(vec![42_u64; 128]);
1212        let mut workers = Vec::new();
1213        for _ in 0..8 {
1214            let shared = value.clone();
1215            let barrier = barrier.clone();
1216            workers.push(std::thread::spawn(move || {
1217                barrier.wait();
1218                assert_eq!(shared.iter().sum::<u64>(), 42 * 128);
1219                drop(shared);
1220            }));
1221        }
1222        // The last worker must synchronize reclamation through the refcount,
1223        // not through the parent's join or an additional surviving reference.
1224        drop(value);
1225        for worker in workers {
1226            worker.join().expect("reader thread");
1227        }
1228    }
1229
1230    #[test]
1231    fn test_thread_safety() {
1232        use std::thread;
1233
1234        let arc = CompactArc::new(0);
1235        let mut handles = vec![];
1236
1237        for _ in 0..10 {
1238            let arc_clone = arc.clone();
1239            handles.push(thread::spawn(move || {
1240                for _ in 0..1000 {
1241                    let _ = *arc_clone;
1242                    let _another = arc_clone.clone();
1243                }
1244            }));
1245        }
1246
1247        for handle in handles {
1248            handle.join().unwrap();
1249        }
1250
1251        drop(arc);
1252    }
1253
1254    // ========================================================================
1255    // DST Tests: str
1256    // ========================================================================
1257
1258    #[test]
1259    fn test_str_basic() {
1260        let s: CompactArc<str> = CompactArc::from_str_slice("hello world");
1261        assert_eq!(&*s, "hello world");
1262        assert_eq!(s.len(), 11);
1263        assert!(!s.is_empty());
1264    }
1265
1266    #[test]
1267    fn test_str_empty() {
1268        let s: CompactArc<str> = CompactArc::from_str_slice("");
1269        assert_eq!(&*s, "");
1270        assert_eq!(s.len(), 0);
1271        assert!(s.is_empty());
1272    }
1273
1274    #[test]
1275    fn test_str_clone() {
1276        let s1: CompactArc<str> = CompactArc::from_str_slice("hello");
1277        let s2 = s1.clone();
1278
1279        assert_eq!(&*s1, "hello");
1280        assert_eq!(&*s2, "hello");
1281        assert!(CompactArc::ptr_eq(&s1, &s2));
1282        assert_eq!(CompactArc::strong_count(&s1), 2);
1283    }
1284
1285    #[test]
1286    fn test_str_drop() {
1287        let s1: CompactArc<str> = CompactArc::from_str_slice("test string");
1288        let s2 = s1.clone();
1289        assert_eq!(CompactArc::strong_count(&s1), 2);
1290
1291        drop(s1);
1292        assert_eq!(CompactArc::strong_count(&s2), 1);
1293        assert_eq!(&*s2, "test string");
1294    }
1295
1296    #[test]
1297    fn test_str_unicode() {
1298        let s: CompactArc<str> = CompactArc::from_str_slice("こんにちは世界");
1299        assert_eq!(&*s, "こんにちは世界");
1300        assert_eq!(s.len(), 21);
1301    }
1302
1303    #[test]
1304    fn test_str_from_impls() {
1305        let s1: CompactArc<str> = CompactArc::from("hello");
1306        let s2: CompactArc<str> = CompactArc::from(String::from("world"));
1307
1308        assert_eq!(&*s1, "hello");
1309        assert_eq!(&*s2, "world");
1310    }
1311
1312    #[test]
1313    fn test_str_thin_pointer() {
1314        // KEY TEST: CompactArc<str> should be 8 bytes (thin pointer!)
1315        assert_eq!(std::mem::size_of::<CompactArc<str>>(), 8);
1316    }
1317
1318    #[test]
1319    fn test_str_equality() {
1320        let s1: CompactArc<str> = CompactArc::from_str_slice("hello");
1321        let s2: CompactArc<str> = CompactArc::from_str_slice("hello");
1322        let s3: CompactArc<str> = CompactArc::from_str_slice("world");
1323
1324        assert_eq!(s1, s2);
1325        assert_ne!(s1, s3);
1326    }
1327
1328    #[test]
1329    fn test_str_hash() {
1330        use std::collections::HashMap;
1331
1332        let s: CompactArc<str> = CompactArc::from_str_slice("key");
1333        let mut map = HashMap::new();
1334        map.insert(s.clone(), "value");
1335
1336        assert_eq!(map.get(&s), Some(&"value"));
1337    }
1338
1339    // ========================================================================
1340    // DST Tests: [T]
1341    // ========================================================================
1342
1343    #[test]
1344    fn test_slice_basic() {
1345        let arr: CompactArc<[i32]> = CompactArc::from_slice(&[1, 2, 3, 4, 5]);
1346        assert_eq!(&*arr, &[1, 2, 3, 4, 5]);
1347        assert_eq!(arr.len(), 5);
1348        assert!(!arr.is_empty());
1349    }
1350
1351    #[test]
1352    fn test_slice_empty() {
1353        let empty: &[i32] = &[];
1354        let arr: CompactArc<[i32]> = CompactArc::from_slice(empty);
1355        assert_eq!(&*arr, empty);
1356        assert_eq!(arr.len(), 0);
1357        assert!(arr.is_empty());
1358    }
1359
1360    #[test]
1361    fn test_slice_clone() {
1362        let arr1: CompactArc<[i32]> = CompactArc::from_slice(&[1, 2, 3]);
1363        let arr2 = arr1.clone();
1364
1365        assert_eq!(&*arr1, &[1, 2, 3]);
1366        assert_eq!(&*arr2, &[1, 2, 3]);
1367        assert!(CompactArc::ptr_eq(&arr1, &arr2));
1368        assert_eq!(CompactArc::strong_count(&arr1), 2);
1369    }
1370
1371    #[test]
1372    fn test_slice_thin_pointer() {
1373        // KEY TEST: CompactArc<[T]> should be 8 bytes (thin pointer!)
1374        assert_eq!(std::mem::size_of::<CompactArc<[i32]>>(), 8);
1375        assert_eq!(std::mem::size_of::<CompactArc<[String]>>(), 8);
1376    }
1377
1378    #[test]
1379    fn test_slice_from_vec() {
1380        let arr: CompactArc<[String]> = CompactArc::from(vec![
1381            String::from("a"),
1382            String::from("b"),
1383            String::from("c"),
1384        ]);
1385        assert_eq!(arr.len(), 3);
1386        assert_eq!(&arr[0], "a");
1387        assert_eq!(&arr[1], "b");
1388        assert_eq!(&arr[2], "c");
1389    }
1390
1391    #[test]
1392    fn test_slice_from_compact_vec() {
1393        let mut compact_vec = CompactVec::new();
1394        compact_vec.push(String::from("x"));
1395        compact_vec.push(String::from("y"));
1396        compact_vec.push(String::from("z"));
1397
1398        let arr: CompactArc<[String]> = CompactArc::from_compact_vec(compact_vec);
1399        assert_eq!(arr.len(), 3);
1400        assert_eq!(&arr[0], "x");
1401        assert_eq!(&arr[1], "y");
1402        assert_eq!(&arr[2], "z");
1403    }
1404
1405    #[test]
1406    fn test_from_compact_vec_moves_elements() {
1407        use std::sync::atomic::{AtomicUsize, Ordering};
1408
1409        static DROP_COUNT: AtomicUsize = AtomicUsize::new(0);
1410        static CLONE_COUNT: AtomicUsize = AtomicUsize::new(0);
1411
1412        #[derive(Debug, PartialEq)]
1413        struct MoveTracker(u32);
1414
1415        impl Clone for MoveTracker {
1416            fn clone(&self) -> Self {
1417                CLONE_COUNT.fetch_add(1, Ordering::SeqCst);
1418                MoveTracker(self.0)
1419            }
1420        }
1421
1422        impl Drop for MoveTracker {
1423            fn drop(&mut self) {
1424                DROP_COUNT.fetch_add(1, Ordering::SeqCst);
1425            }
1426        }
1427
1428        DROP_COUNT.store(0, Ordering::SeqCst);
1429        CLONE_COUNT.store(0, Ordering::SeqCst);
1430
1431        {
1432            let mut compact_vec = CompactVec::new();
1433            compact_vec.push(MoveTracker(1));
1434            compact_vec.push(MoveTracker(2));
1435            compact_vec.push(MoveTracker(3));
1436
1437            let arr: CompactArc<[MoveTracker]> = CompactArc::from_compact_vec(compact_vec);
1438
1439            // Verify elements are accessible
1440            assert_eq!(arr[0].0, 1);
1441            assert_eq!(arr[1].0, 2);
1442            assert_eq!(arr[2].0, 3);
1443
1444            // No clones should have happened (elements were moved)
1445            assert_eq!(
1446                CLONE_COUNT.load(Ordering::SeqCst),
1447                0,
1448                "from_compact_vec should move, not clone"
1449            );
1450        }
1451
1452        // Only 3 drops: the elements in the CompactArc
1453        assert_eq!(DROP_COUNT.load(Ordering::SeqCst), 3);
1454    }
1455
1456    #[test]
1457    fn test_slice_drop_elements() {
1458        use std::sync::atomic::{AtomicUsize, Ordering};
1459
1460        static DROP_COUNT: AtomicUsize = AtomicUsize::new(0);
1461
1462        #[derive(Clone)]
1463        struct DropCounter;
1464        impl Drop for DropCounter {
1465            fn drop(&mut self) {
1466                DROP_COUNT.fetch_add(1, Ordering::SeqCst);
1467            }
1468        }
1469
1470        DROP_COUNT.store(0, Ordering::SeqCst);
1471
1472        {
1473            let arr: CompactArc<[DropCounter]> =
1474                CompactArc::from_slice(&[DropCounter, DropCounter, DropCounter]);
1475            let _arr2 = arr.clone();
1476        }
1477
1478        // 3 from original slice + 3 from arc = 6
1479        assert_eq!(DROP_COUNT.load(Ordering::SeqCst), 6);
1480    }
1481
1482    #[test]
1483    fn test_from_slice_panic_safety() {
1484        use std::sync::atomic::{AtomicUsize, Ordering};
1485
1486        static DROP_COUNT: AtomicUsize = AtomicUsize::new(0);
1487        static CLONE_COUNT: AtomicUsize = AtomicUsize::new(0);
1488
1489        #[derive(Debug)]
1490        struct PanicOnThird(u32);
1491
1492        impl Clone for PanicOnThird {
1493            fn clone(&self) -> Self {
1494                let count = CLONE_COUNT.fetch_add(1, Ordering::SeqCst);
1495                if count == 2 {
1496                    panic!("Panic on third clone!");
1497                }
1498                PanicOnThird(self.0)
1499            }
1500        }
1501
1502        impl Drop for PanicOnThird {
1503            fn drop(&mut self) {
1504                DROP_COUNT.fetch_add(1, Ordering::SeqCst);
1505            }
1506        }
1507
1508        DROP_COUNT.store(0, Ordering::SeqCst);
1509        CLONE_COUNT.store(0, Ordering::SeqCst);
1510
1511        let slice = &[
1512            PanicOnThird(1),
1513            PanicOnThird(2),
1514            PanicOnThird(3),
1515            PanicOnThird(4),
1516        ];
1517
1518        let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
1519            let _: CompactArc<[PanicOnThird]> = CompactArc::from_slice(slice);
1520        }));
1521
1522        assert!(result.is_err(), "Should have panicked");
1523
1524        // Verify panic safety: 2 successfully cloned elements should be dropped
1525        // (the 3rd clone panicked before being written)
1526        let drops_from_cleanup = DROP_COUNT.load(Ordering::SeqCst);
1527        assert_eq!(
1528            drops_from_cleanup, 2,
1529            "Should have dropped 2 successfully cloned elements"
1530        );
1531    }
1532
1533    #[test]
1534    fn test_from_vec_moves_elements() {
1535        use std::sync::atomic::{AtomicUsize, Ordering};
1536
1537        static DROP_COUNT: AtomicUsize = AtomicUsize::new(0);
1538        static CLONE_COUNT: AtomicUsize = AtomicUsize::new(0);
1539
1540        #[derive(Debug, PartialEq)]
1541        struct MoveTracker(u32);
1542
1543        impl Clone for MoveTracker {
1544            fn clone(&self) -> Self {
1545                CLONE_COUNT.fetch_add(1, Ordering::SeqCst);
1546                MoveTracker(self.0)
1547            }
1548        }
1549
1550        impl Drop for MoveTracker {
1551            fn drop(&mut self) {
1552                DROP_COUNT.fetch_add(1, Ordering::SeqCst);
1553            }
1554        }
1555
1556        DROP_COUNT.store(0, Ordering::SeqCst);
1557        CLONE_COUNT.store(0, Ordering::SeqCst);
1558
1559        {
1560            let vec = vec![MoveTracker(1), MoveTracker(2), MoveTracker(3)];
1561            let arr: CompactArc<[MoveTracker]> = CompactArc::from_vec(vec);
1562
1563            // Verify elements are accessible (compare inner values to avoid creating temporaries)
1564            assert_eq!(arr[0].0, 1);
1565            assert_eq!(arr[1].0, 2);
1566            assert_eq!(arr[2].0, 3);
1567
1568            // No clones should have happened (elements were moved)
1569            assert_eq!(
1570                CLONE_COUNT.load(Ordering::SeqCst),
1571                0,
1572                "from_vec should move, not clone"
1573            );
1574        }
1575
1576        // Only 3 drops: the elements in the CompactArc
1577        assert_eq!(DROP_COUNT.load(Ordering::SeqCst), 3);
1578    }
1579
1580    #[test]
1581    fn test_dst_send_sync() {
1582        fn assert_send<T: Send>() {}
1583        fn assert_sync<T: Sync>() {}
1584
1585        assert_send::<CompactArc<str>>();
1586        assert_sync::<CompactArc<str>>();
1587        assert_send::<CompactArc<[i32]>>();
1588        assert_sync::<CompactArc<[i32]>>();
1589    }
1590
1591    #[test]
1592    fn test_str_thread_safety() {
1593        use std::thread;
1594
1595        let s: CompactArc<str> = CompactArc::from_str_slice("shared string");
1596        let mut handles = vec![];
1597
1598        for _ in 0..10 {
1599            let s_clone = s.clone();
1600            handles.push(thread::spawn(move || {
1601                for _ in 0..1000 {
1602                    let _ = s_clone.len();
1603                    let _another = s_clone.clone();
1604                }
1605            }));
1606        }
1607
1608        for handle in handles {
1609            handle.join().unwrap();
1610        }
1611
1612        drop(s);
1613    }
1614}