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