Skip to main content

i_slint_core/
sharedvector.rs

1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
3
4//! module for the SharedVector and related things
5#![allow(unsafe_code)]
6#![warn(missing_docs)]
7use core::fmt::Debug;
8use core::mem::MaybeUninit;
9use core::ops::Deref;
10use core::ptr::NonNull;
11
12use portable_atomic as atomic;
13
14#[repr(C)]
15struct SharedVectorHeader {
16    refcount: atomic::AtomicIsize,
17    size: usize,
18    capacity: usize,
19}
20
21#[repr(C)]
22struct SharedVectorInner<T> {
23    header: SharedVectorHeader,
24    data: MaybeUninit<T>,
25}
26
27fn compute_inner_layout<T>(capacity: usize) -> core::alloc::Layout {
28    core::alloc::Layout::new::<SharedVectorHeader>()
29        .extend(core::alloc::Layout::array::<T>(capacity).unwrap())
30        .unwrap()
31        .0
32}
33
34/// Returns a raw pointer to the data with full allocation provenance.
35///
36/// Must not go through `&SharedVectorInner<T>` or `&mut SharedVectorInner<T>` because
37/// the declared struct size is smaller than the actual allocation.
38fn data_ptr<T>(inner: NonNull<SharedVectorInner<T>>) -> *mut T {
39    // Safety: inner.as_ptr() is a valid raw pointer; &raw mut avoids creating a reference.
40    unsafe { &raw mut (*inner.as_ptr()).data as *mut T }
41}
42
43/// # Safety
44/// Caller must ensure refcount is 0 and no other references to `inner` exist.
45unsafe fn drop_inner<T>(inner: NonNull<SharedVectorInner<T>>) {
46    unsafe {
47        debug_assert_eq!((*inner.as_ptr()).header.refcount.load(atomic::Ordering::Relaxed), 0);
48        let data = data_ptr(inner);
49        let size = (*inner.as_ptr()).header.size;
50        for x in 0..size {
51            core::ptr::drop_in_place(data.add(x));
52        }
53        alloc::alloc::dealloc(
54            inner.as_ptr() as *mut u8,
55            compute_inner_layout::<T>((*inner.as_ptr()).header.capacity),
56        )
57    }
58}
59
60/// Allocate the memory for the SharedVector with the given capacity. Return the inner with size and refcount set to 1
61fn alloc_with_capacity<T>(capacity: usize) -> NonNull<SharedVectorInner<T>> {
62    let ptr = unsafe { ::alloc::alloc::alloc(compute_inner_layout::<T>(capacity)) };
63    assert!(!ptr.is_null(), "allocation of {capacity:?} bytes failed");
64    unsafe {
65        core::ptr::write(
66            ptr as *mut SharedVectorHeader,
67            SharedVectorHeader { refcount: 1.into(), size: 0, capacity },
68        );
69    }
70    NonNull::new(ptr).unwrap().cast()
71}
72
73/// Return a new capacity suitable for this vector
74/// Loosely based on alloc::raw_vec::RawVec::grow_amortized.
75fn capacity_for_grow(current_cap: usize, required_cap: usize, elem_size: usize) -> usize {
76    if current_cap >= required_cap {
77        return current_cap;
78    }
79    let cap = core::cmp::max(current_cap * 2, required_cap);
80    let min_non_zero_cap = if elem_size == 1 {
81        8
82    } else if elem_size <= 1024 {
83        4
84    } else {
85        1
86    };
87    core::cmp::max(min_non_zero_cap, cap)
88}
89
90#[repr(C)]
91/// SharedVector holds a reference-counted read-only copy of `[T]`.
92pub struct SharedVector<T> {
93    inner: NonNull<SharedVectorInner<T>>,
94}
95
96// Safety: We use atomic reference counting, and if T is Send and Sync, we can send the vector to another thread
97unsafe impl<T: Send + Sync> Send for SharedVector<T> {}
98// Safety: We use atomic reference counting, and if T is Send and Sync, we can access the vector from multiple threads
99unsafe impl<T: Send + Sync> Sync for SharedVector<T> {}
100
101impl<T> Drop for SharedVector<T> {
102    fn drop(&mut self) {
103        // Safety: inner is always a valid pointer (either a real allocation or SHARED_NULL).
104        unsafe {
105            let header = &raw const (*self.inner.as_ptr()).header;
106            if (*header).refcount.load(atomic::Ordering::Relaxed) < 0 {
107                return;
108            }
109            if (*header).refcount.fetch_sub(1, atomic::Ordering::SeqCst) == 1 {
110                drop_inner(self.inner)
111            }
112        }
113    }
114}
115
116impl<T> Clone for SharedVector<T> {
117    fn clone(&self) -> Self {
118        // Safety: inner is always a valid pointer (either a real allocation or SHARED_NULL).
119        unsafe {
120            let header = &raw const (*self.inner.as_ptr()).header;
121            if (*header).refcount.load(atomic::Ordering::Relaxed) > 0 {
122                (*header).refcount.fetch_add(1, atomic::Ordering::SeqCst);
123            }
124            SharedVector { inner: self.inner }
125        }
126    }
127}
128
129impl<T> SharedVector<T> {
130    /// Create a new empty array with a pre-allocated capacity in number of items
131    pub fn with_capacity(capacity: usize) -> Self {
132        Self { inner: alloc_with_capacity(capacity) }
133    }
134
135    fn as_ptr(&self) -> *const T {
136        data_ptr(self.inner)
137    }
138
139    /// Number of elements in the array
140    pub fn len(&self) -> usize {
141        // Safety: header is always fully allocated (even for SHARED_NULL).
142        unsafe { (*self.inner.as_ptr()).header.size }
143    }
144
145    /// Return true if the SharedVector is empty
146    pub fn is_empty(&self) -> bool {
147        self.len() == 0
148    }
149
150    /// Return a slice to the array
151    pub fn as_slice(&self) -> &[T] {
152        if self.is_empty() {
153            &[]
154        } else {
155            // Safety: len > 0 ensures data_ptr is valid for len elements.
156            unsafe { core::slice::from_raw_parts(self.as_ptr(), self.len()) }
157        }
158    }
159
160    /// Returns the number of elements the vector can hold without reallocating, when not shared
161    fn capacity(&self) -> usize {
162        // Safety: header is always fully allocated (even for SHARED_NULL).
163        unsafe { (*self.inner.as_ptr()).header.capacity }
164    }
165}
166
167impl<T: Clone + PartialEq> SharedVector<T> {
168    /// Replaces `from` by `to` in `self` `count` times
169    /// `count` - number of times to do the replacements
170    pub(crate) fn replace_range(&mut self, from: &[T], to: &[T], mut count: usize) {
171        if from.is_empty() || count == 0 || from.len() != to.len() {
172            return;
173        }
174        let s = self.make_mut_slice();
175        if s.len() < from.len() {
176            return;
177        }
178
179        let mut index = 0;
180        let from_len = from.len();
181        let max_start = s.len() - from_len;
182
183        while index <= max_start && count > 0 {
184            if s[index..index + from_len] == *from {
185                for (dst, src) in s[index..index + from_len].iter_mut().zip(to.iter()) {
186                    *dst = src.clone();
187                }
188                count -= 1;
189                index += from_len;
190            } else {
191                index += 1;
192            }
193        }
194    }
195}
196
197impl<T: Clone> SharedVector<T> {
198    /// Create a SharedVector from a slice
199    pub fn from_slice(slice: &[T]) -> SharedVector<T> {
200        Self::from(slice)
201    }
202
203    /// Ensure that the reference count is 1 so the array can be changed.
204    /// If that's not the case, the array will be cloned
205    fn detach(&mut self, new_capacity: usize) {
206        // Acquire: if refcount == 1, synchronize with the Release in the last Drop to
207        // ensure prior writes to size/data are visible before we mutate.
208        let is_shared =
209            unsafe { (*self.inner.as_ptr()).header.refcount.load(atomic::Ordering::Acquire) } != 1;
210        if !is_shared && new_capacity <= self.capacity() {
211            return;
212        }
213        let mut new_array = SharedVector::with_capacity(new_capacity);
214        core::mem::swap(&mut self.inner, &mut new_array.inner);
215        let mut size = 0;
216        for x in new_array.into_iter() {
217            assert_ne!(size, new_capacity);
218            unsafe {
219                core::ptr::write(data_ptr(self.inner).add(size), x);
220                size += 1;
221                (*self.inner.as_ptr()).header.size = size;
222            }
223            if size == new_capacity {
224                break;
225            }
226        }
227    }
228
229    /// Return a mutable slice to the array. If the array was shared, this will make a copy of the array.
230    pub fn make_mut_slice(&mut self) -> &mut [T] {
231        self.detach(self.len());
232        unsafe { core::slice::from_raw_parts_mut(self.as_ptr() as *mut T, self.len()) }
233    }
234
235    /// Add an element to the array. If the array was shared, this will make a copy of the array.
236    pub fn push(&mut self, value: T) {
237        self.detach(capacity_for_grow(self.capacity(), self.len() + 1, core::mem::size_of::<T>()));
238        // Safety: detach ensures exclusive ownership and sufficient capacity.
239        unsafe {
240            let size = (*self.inner.as_ptr()).header.size;
241            core::ptr::write(data_ptr(self.inner).add(size), value);
242            (*self.inner.as_ptr()).header.size = size + 1;
243        }
244    }
245
246    /// Removes the element at the given index from the array and returns it.
247    /// If the array was shared, this will make a copy of the array.
248    ///
249    /// Panics if `row` is out of bounds.
250    pub fn remove(&mut self, row: usize) -> T {
251        let len = self.len();
252        if row >= len {
253            panic!("removal index (is {row}) should be < len (is {len})");
254        }
255        self.detach(len);
256        unsafe {
257            let data = data_ptr(self.inner);
258            let value = core::ptr::read(data.add(row));
259            let size = (*self.inner.as_ptr()).header.size;
260            core::ptr::copy(data.add(row + 1), data.add(row), size - 1 - row);
261            (*self.inner.as_ptr()).header.size = size - 1;
262            value
263        }
264    }
265
266    /// Inserts the element at the given index in the array, shifting the following elements.
267    /// If the array was shared, this will make a copy of the array.
268    ///
269    /// Panics if `row > len`.
270    pub fn insert(&mut self, row: usize, value: T) {
271        let len = self.len();
272        if row > len {
273            panic!("insertion index (is {row}) should be <= len (is {len})");
274        }
275        self.detach(capacity_for_grow(self.capacity(), self.len() + 1, core::mem::size_of::<T>()));
276        unsafe {
277            let data = data_ptr(self.inner);
278            let size = (*self.inner.as_ptr()).header.size;
279            core::ptr::copy(data.add(row), data.add(row + 1), size - row);
280            core::ptr::write(data.add(row), value);
281            (*self.inner.as_ptr()).header.size = size + 1;
282        }
283    }
284
285    /// Removes last element from the array and returns it.
286    /// If the array was shared, this will make a copy of the array.
287    pub fn pop(&mut self) -> Option<T> {
288        if self.is_empty() {
289            None
290        } else {
291            self.detach(self.len());
292            // Safety: detach ensures exclusive ownership; len > 0 guarantees an element exists.
293            unsafe {
294                let size = (*self.inner.as_ptr()).header.size - 1;
295                (*self.inner.as_ptr()).header.size = size;
296                Some(core::ptr::read(data_ptr(self.inner).add(size)))
297            }
298        }
299    }
300
301    /// Resize the array to the given size.
302    /// If the array was smaller new elements will be initialized with the value.
303    /// If the array was bigger, extra elements will be discarded
304    ///
305    /// ```
306    /// use i_slint_core::SharedVector;
307    /// let mut shared_vector = SharedVector::<u32>::from_slice(&[1, 2, 3]);
308    /// shared_vector.resize(5, 8);
309    /// assert_eq!(shared_vector.as_slice(), &[1, 2, 3, 8, 8]);
310    /// shared_vector.resize(2, 0);
311    /// assert_eq!(shared_vector.as_slice(), &[1, 2]);
312    /// ```
313    pub fn resize(&mut self, new_len: usize, value: T) {
314        if self.len() == new_len {
315            return;
316        }
317        self.detach(new_len);
318        // Safety: detach ensured that the array is not shared.
319        let header = unsafe { &mut (*self.inner.as_ptr()).header };
320
321        if header.size >= new_len {
322            self.shrink(new_len);
323        } else {
324            let data_ptr = data_ptr(self.inner);
325            while header.size < new_len {
326                // Safety: The array must have a capacity of at least new_len because of the detach call earlier
327                unsafe {
328                    core::ptr::write(data_ptr.add(header.size), value.clone());
329                }
330                header.size += 1;
331            }
332        }
333    }
334
335    fn shrink(&mut self, new_len: usize) {
336        if self.len() == new_len {
337            return;
338        }
339
340        assert!(
341            unsafe { (*self.inner.as_ptr()).header.refcount.load(atomic::Ordering::Relaxed) } == 1
342        );
343        // Safety: caller (and above assert) must ensure that the array is not shared.
344        let header = unsafe { &mut (*self.inner.as_ptr()).header };
345        let data_ptr = data_ptr(self.inner);
346
347        while header.size > new_len {
348            header.size -= 1;
349            // Safety: The array was of size header.size, so there should be an element there
350            unsafe {
351                core::ptr::drop_in_place(data_ptr.add(header.size));
352            }
353        }
354    }
355
356    /// Clears the vector and removes all elements.
357    pub fn clear(&mut self) {
358        let is_shared =
359            unsafe { (*self.inner.as_ptr()).header.refcount.load(atomic::Ordering::Acquire) } != 1;
360        if is_shared {
361            *self = SharedVector::default();
362        } else {
363            self.shrink(0)
364        }
365    }
366
367    /// Reserves capacity for at least `additional` bytes more than the current vector's length.
368    pub fn reserve(&mut self, additional: usize) {
369        self.detach((self.len() + additional).max(self.capacity()))
370    }
371}
372
373impl<T> Deref for SharedVector<T> {
374    type Target = [T];
375    fn deref(&self) -> &Self::Target {
376        self.as_slice()
377    }
378}
379
380/* FIXME: is this a good idea to implement DerefMut knowing what it might detach?
381impl<T> DerefMut for SharedVector<T> {
382    fn deref_mut(&mut self) -> &mut Self::Target {
383        self.as_mut_slice()
384    }
385}*/
386
387impl<T: Clone> From<&[T]> for SharedVector<T> {
388    fn from(slice: &[T]) -> Self {
389        let capacity = slice.len();
390        let result = Self::with_capacity(capacity);
391        for x in slice {
392            unsafe {
393                let size = (*result.inner.as_ptr()).header.size;
394                core::ptr::write(data_ptr(result.inner).add(size), x.clone());
395                (*result.inner.as_ptr()).header.size = size + 1;
396            }
397        }
398        result
399    }
400}
401
402impl<T, const N: usize> From<[T; N]> for SharedVector<T> {
403    fn from(array: [T; N]) -> Self {
404        array.into_iter().collect()
405    }
406}
407
408impl<T> FromIterator<T> for SharedVector<T> {
409    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
410        let mut iter = iter.into_iter();
411        let mut capacity = iter.size_hint().0;
412        let mut result = Self::with_capacity(capacity);
413        let mut size = 0;
414        while let Some(x) = iter.next() {
415            if size >= capacity {
416                capacity = capacity_for_grow(
417                    capacity,
418                    size + 1 + iter.size_hint().0,
419                    core::mem::size_of::<T>(),
420                );
421                unsafe {
422                    (*result.inner.as_ptr()).header.refcount.store(0, atomic::Ordering::Relaxed)
423                };
424                let mut iter = IntoIter(IntoIterInner::UnShared(result.inner, 0));
425                result.inner = alloc_with_capacity::<T>(capacity);
426                match &mut iter.0 {
427                    IntoIterInner::UnShared(old_inner, begin) => {
428                        let old_data = data_ptr(*old_inner);
429                        while *begin < size {
430                            unsafe {
431                                core::ptr::write(
432                                    data_ptr(result.inner).add(*begin),
433                                    core::ptr::read(old_data.add(*begin)),
434                                );
435                                *begin += 1;
436                                (*result.inner.as_ptr()).header.size = *begin;
437                            }
438                        }
439                    }
440                    _ => unreachable!(),
441                }
442            }
443            debug_assert_eq!(result.len(), size);
444            debug_assert!(result.capacity() > size);
445            unsafe {
446                core::ptr::write(data_ptr(result.inner).add(size), x);
447                size += 1;
448                (*result.inner.as_ptr()).header.size = size;
449            }
450        }
451        result
452    }
453}
454
455impl<T: Clone> Extend<T> for SharedVector<T> {
456    fn extend<X: IntoIterator<Item = T>>(&mut self, iter: X) {
457        let iter = iter.into_iter();
458        let hint = iter.size_hint().0;
459        if hint > 0 {
460            self.detach(capacity_for_grow(
461                self.capacity(),
462                self.len() + hint,
463                core::mem::size_of::<T>(),
464            ));
465        }
466        for item in iter {
467            self.push(item);
468        }
469    }
470}
471
472/// The empty singleton that `SharedVector::default()` for every element type T points to.
473/// It only consists of a header, but the pointer is dereferenced as `SharedVectorInner<T>`,
474/// so the static must satisfy the alignment of the most aligned `SharedVectorInner<T>` in
475/// the program - not just `SharedVectorHeader`'s own alignment.
476#[repr(C, align(16))]
477struct SharedNull(SharedVectorHeader);
478
479static SHARED_NULL: SharedNull =
480    SharedNull(SharedVectorHeader { refcount: atomic::AtomicIsize::new(-1), size: 0, capacity: 0 });
481
482impl<T> Default for SharedVector<T> {
483    fn default() -> Self {
484        const {
485            assert!(
486                core::mem::align_of::<SharedVectorInner<T>>()
487                    <= core::mem::align_of::<SharedNull>(),
488                "SharedVector element type is more aligned than the empty singleton"
489            );
490        }
491        SharedVector { inner: NonNull::from(&SHARED_NULL).cast() }
492    }
493}
494
495impl<T: Debug> Debug for SharedVector<T> {
496    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
497        self.as_slice().fmt(f)
498    }
499}
500
501impl<T> AsRef<[T]> for SharedVector<T> {
502    #[inline]
503    fn as_ref(&self) -> &[T] {
504        self.as_slice()
505    }
506}
507
508impl<T, U> PartialEq<U> for SharedVector<T>
509where
510    U: ?Sized + AsRef<[T]>,
511    T: PartialEq,
512{
513    fn eq(&self, other: &U) -> bool {
514        self.as_slice() == other.as_ref()
515    }
516}
517
518impl<T: Eq> Eq for SharedVector<T> {}
519
520impl<T: Clone> IntoIterator for SharedVector<T> {
521    type Item = T;
522    type IntoIter = IntoIter<T>;
523    fn into_iter(self) -> Self::IntoIter {
524        IntoIter(unsafe {
525            if (*self.inner.as_ptr()).header.refcount.load(atomic::Ordering::Acquire) == 1 {
526                let inner = self.inner;
527                core::mem::forget(self);
528                (*inner.as_ptr()).header.refcount.store(0, atomic::Ordering::Relaxed);
529                IntoIterInner::UnShared(inner, 0)
530            } else {
531                IntoIterInner::Shared(self, 0)
532            }
533        })
534    }
535}
536
537#[cfg(feature = "serde")]
538use serde::ser::SerializeSeq;
539#[cfg(feature = "serde")]
540impl<T> serde::Serialize for SharedVector<T>
541where
542    T: serde::Serialize,
543{
544    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
545    where
546        S: serde::Serializer,
547    {
548        let mut seq = serializer.serialize_seq(Some(self.len()))?;
549        for item in self.iter() {
550            seq.serialize_element(item)?;
551        }
552        seq.end()
553    }
554}
555
556#[cfg(feature = "serde")]
557impl<'de, T> serde::Deserialize<'de> for SharedVector<T>
558where
559    T: Clone + serde::Deserialize<'de>,
560{
561    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
562    where
563        D: serde::Deserializer<'de>,
564    {
565        let mut elements: alloc::vec::Vec<T> = serde::Deserialize::deserialize(deserializer)?;
566        let mut shared_vec = SharedVector::with_capacity(elements.len());
567        for elem in elements.drain(..) {
568            shared_vec.push(elem);
569        }
570        Ok(shared_vec)
571    }
572}
573
574enum IntoIterInner<T> {
575    Shared(SharedVector<T>, usize),
576    // Elements up to the usize member are already moved out
577    UnShared(NonNull<SharedVectorInner<T>>, usize),
578}
579
580impl<T> Drop for IntoIterInner<T> {
581    fn drop(&mut self) {
582        match self {
583            IntoIterInner::Shared(..) => { /* drop of SharedVector takes care of it */ }
584            IntoIterInner::UnShared(inner, begin) => unsafe {
585                debug_assert_eq!(
586                    (*inner.as_ptr()).header.refcount.load(atomic::Ordering::Relaxed),
587                    0
588                );
589                let data_ptr = data_ptr(*inner);
590                for x in (*begin)..(*inner.as_ptr()).header.size {
591                    core::ptr::drop_in_place(data_ptr.add(x));
592                }
593                ::alloc::alloc::dealloc(
594                    inner.as_ptr() as *mut u8,
595                    compute_inner_layout::<T>((*inner.as_ptr()).header.capacity),
596                )
597            },
598        }
599    }
600}
601
602/// An iterator that moves out of a SharedVector.
603///
604/// This `struct` is created by the `into_iter` method on [`SharedVector`] (provided
605/// by the [`IntoIterator`] trait).
606pub struct IntoIter<T>(IntoIterInner<T>);
607
608impl<T: Clone> Iterator for IntoIter<T> {
609    type Item = T;
610
611    fn next(&mut self) -> Option<Self::Item> {
612        match &mut self.0 {
613            IntoIterInner::Shared(array, moved) => {
614                let result = array.as_slice().get(*moved).cloned();
615                *moved += 1;
616                result
617            }
618            IntoIterInner::UnShared(inner, begin) => unsafe {
619                if *begin < (*inner.as_ptr()).header.size {
620                    let data_ptr = data_ptr(*inner);
621                    let r = core::ptr::read(data_ptr.add(*begin));
622                    *begin += 1;
623                    Some(r)
624                } else {
625                    None
626                }
627            },
628        }
629    }
630}
631
632#[test]
633fn simple_test() {
634    let x: SharedVector<i32> = SharedVector::from([1, 2, 3]);
635    let y: SharedVector<i32> = SharedVector::from([3, 2, 1]);
636    assert_eq!(x, x.clone());
637    assert_ne!(x, y);
638    let z: [i32; 3] = [1, 2, 3];
639    assert_eq!(z, x.as_slice());
640    let vec: std::vec::Vec<i32> = std::vec![1, 2, 3];
641    assert_eq!(x, vec);
642    let def: SharedVector<i32> = Default::default();
643    assert_eq!(def, SharedVector::<i32>::default());
644    assert_ne!(def, x);
645}
646
647#[test]
648fn push_test() {
649    let mut x: SharedVector<i32> = SharedVector::from([1, 2, 3]);
650    let y = x.clone();
651    x.push(4);
652    x.push(5);
653    x.push(6);
654    assert_eq!(x.as_slice(), &[1, 2, 3, 4, 5, 6]);
655    assert_eq!(y.as_slice(), &[1, 2, 3]);
656}
657
658#[test]
659fn remove_test() {
660    let mut x: SharedVector<i32> = SharedVector::from([1, 2, 3, 4, 5, 6]);
661    let y = x.clone();
662    assert_eq!(x.remove(0), 1);
663    assert_eq!(x.remove(1), 3);
664    x.push(42);
665    assert_eq!(x.remove(2), 5);
666    assert_eq!(x.as_slice(), &[2, 4, 6, 42]);
667    assert_eq!(y.as_slice(), &[1, 2, 3, 4, 5, 6]);
668}
669
670#[test]
671#[should_panic(expected = "removal index (is 3) should be < len (is 3)")]
672fn remove_out_of_bounds_test() {
673    let mut x: SharedVector<i32> = SharedVector::from([1, 2, 3]);
674    x.remove(3);
675}
676
677#[test]
678fn insert_test() {
679    let mut x: SharedVector<i32> = SharedVector::from([1, 2, 3]);
680    let y = x.clone();
681    x.insert(0, 42);
682    assert_eq!(x.as_slice(), &[42, 1, 2, 3]);
683    x.insert(2, 24);
684    x.insert(5, 84);
685    assert_eq!(x.as_slice(), &[42, 1, 24, 2, 3, 84]);
686    assert_eq!(y.as_slice(), &[1, 2, 3]);
687}
688
689#[test]
690#[should_panic(expected = "insertion index (is 4) should be <= len (is 3)")]
691fn insert_out_of_bounds_test() {
692    let mut x: SharedVector<i32> = SharedVector::from([1, 2, 3]);
693    x.insert(4, 42);
694}
695
696#[test]
697#[should_panic]
698#[cfg_attr(miri, ignore)] // Miri aborts on large allocations before the panic can fire
699fn invalid_capacity_test() {
700    let _: SharedVector<u8> = SharedVector::with_capacity(usize::MAX / 2 - 1000);
701}
702
703#[test]
704fn collect_from_iter_with_no_size_hint() {
705    use std::string::{String, ToString};
706    struct NoSizeHintIter<'a> {
707        data: &'a [&'a str],
708        i: usize,
709    }
710
711    impl Iterator for NoSizeHintIter<'_> {
712        type Item = String;
713
714        fn next(&mut self) -> Option<Self::Item> {
715            if self.i >= self.data.len() {
716                return None;
717            }
718            let item = self.data[self.i];
719            self.i += 1;
720            Some(item.to_string())
721        }
722
723        fn size_hint(&self) -> (usize, Option<usize>) {
724            (0, None)
725        }
726    }
727
728    // 5 elements to be above the initial "grow"-capacity of 4 and thus require one realloc.
729    let input = NoSizeHintIter { data: &["Hello", "sweet", "world", "of", "iterators"], i: 0 };
730
731    let shared_vec: SharedVector<String> = input.collect();
732    assert_eq!(shared_vec.as_slice(), &["Hello", "sweet", "world", "of", "iterators"]);
733}
734
735#[test]
736fn test_capacity_grows_only_when_needed() {
737    let mut vec: SharedVector<u8> = SharedVector::with_capacity(2);
738    vec.push(0);
739    assert_eq!(vec.capacity(), 2);
740    vec.push(0);
741    assert_eq!(vec.capacity(), 2);
742    vec.push(0);
743    assert_eq!(vec.len(), 3);
744    assert!(vec.capacity() > 2);
745}
746
747#[test]
748fn test_vector_clear() {
749    let mut vec: SharedVector<std::string::String> = Default::default();
750    vec.clear();
751    vec.push("Hello".into());
752    vec.push("World".into());
753    vec.push("of".into());
754    vec.push("Vectors".into());
755
756    let mut copy = vec.clone();
757
758    assert_eq!(vec.len(), 4);
759    let orig_cap = vec.capacity();
760    assert!(orig_cap >= vec.len());
761    vec.clear();
762    assert_eq!(vec.len(), 0);
763    assert_eq!(vec.capacity(), 0); // vec was shared, so start with new empty vector.
764    vec.push("Welcome back".into());
765    assert_eq!(vec.len(), 1);
766    assert!(vec.capacity() >= vec.len());
767
768    assert_eq!(copy.len(), 4);
769    assert_eq!(copy.capacity(), orig_cap);
770    copy.clear(); // copy is not shared (anymore), retain capacity.
771    assert_eq!(copy.capacity(), orig_cap);
772}
773
774#[test]
775fn pop_test() {
776    let mut x: SharedVector<i32> = SharedVector::from([1, 2, 3]);
777    let y = x.clone();
778    assert_eq!(x.pop(), Some(3));
779    assert_eq!(x.pop(), Some(2));
780    assert_eq!(x.pop(), Some(1));
781    assert_eq!(x.pop(), None);
782    assert!(x.is_empty());
783    assert_eq!(y.as_slice(), &[1, 2, 3]);
784}
785
786#[cfg(feature = "ffi")]
787pub(crate) mod ffi {
788    use super::*;
789
790    #[unsafe(no_mangle)]
791    /// This function is used for the low-level C++ interface to allocate the backing vector of a SharedVector.
792    pub unsafe extern "C" fn slint_shared_vector_allocate(size: usize, align: usize) -> *mut u8 {
793        unsafe { alloc::alloc::alloc(alloc::alloc::Layout::from_size_align(size, align).unwrap()) }
794    }
795
796    #[unsafe(no_mangle)]
797    /// This function is used for the low-level C++ interface to deallocate the backing vector of a SharedVector
798    pub unsafe extern "C" fn slint_shared_vector_free(ptr: *mut u8, size: usize, align: usize) {
799        unsafe {
800            alloc::alloc::dealloc(ptr, alloc::alloc::Layout::from_size_align(size, align).unwrap())
801        }
802    }
803
804    #[unsafe(no_mangle)]
805    /// This function is used for the low-level C++ interface to initialize the empty SharedVector.
806    pub unsafe extern "C" fn slint_shared_vector_empty() -> *const u8 {
807        &SHARED_NULL as *const _ as *const u8
808    }
809}
810
811#[cfg(feature = "serde")]
812#[test]
813fn test_serialize_deserialize_sharedvector() {
814    let v = SharedVector::from([1, 2, 3]);
815    let serialized = serde_json::to_string(&v).unwrap();
816    let deserialized: SharedVector<i32> = serde_json::from_str(&serialized).unwrap();
817    assert_eq!(v, deserialized);
818}
819
820#[test]
821fn test_reserve() {
822    let mut v = SharedVector::from([1, 2, 3]);
823    assert_eq!(v.capacity(), 3);
824    v.reserve(1);
825    assert_eq!(v.capacity(), 4);
826    assert_eq!(v.len(), 3);
827    v.push(4);
828    v.push(5);
829    assert_eq!(v.len(), 5);
830    assert_eq!(v.capacity(), 8);
831    v.reserve(1);
832    assert_eq!(v.capacity(), 8);
833    v.reserve(8);
834    assert_eq!(v.len(), 5);
835    assert_eq!(v.capacity(), 13);
836}
837
838#[test]
839fn test_replace_range_all_matches() {
840    let mut v = SharedVector::from([1, 2, 3, 1, 2, 3, 1, 2, 3]);
841    v.replace_range(&[1, 2, 3], &[4, 5, 6], usize::MAX);
842    assert_eq!(v.as_slice(), &[4, 5, 6, 4, 5, 6, 4, 5, 6]);
843}
844
845#[test]
846fn test_replace_range_count_limit() {
847    let mut v = SharedVector::from([1, 2, 3, 1, 2, 3, 1, 2, 3]);
848    v.replace_range(&[1, 2, 3], &[4, 5, 6], 2);
849    assert_eq!(v.as_slice(), &[4, 5, 6, 4, 5, 6, 1, 2, 3]);
850}
851
852#[test]
853fn test_replace_range_non_overlapping() {
854    let mut v = SharedVector::from([1, 1, 1]);
855    v.replace_range(&[1, 1], &[2, 2], usize::MAX);
856    assert_eq!(v.as_slice(), &[2, 2, 1]);
857}
858
859#[test]
860fn test_replace_range() {
861    let mut v = SharedVector::from([1, 2, 3, 4]);
862    v.replace_range(&[2, 3, 4, 5], &[7, 8, 9, 9], 1);
863    assert_eq!(v.as_slice(), &[1, 2, 3, 4]);
864}
865
866#[test]
867fn test_aligned_element_type() {
868    // The empty singleton behind `SharedVector::default()` must satisfy the element
869    // type's alignment (it is dereferenced as `SharedVectorInner<T>`). Use an element
870    // type with the maximum supported alignment so Miri catches a misaligned singleton
871    // on any host, like the 8-aligned element types on wasm32 did.
872    #[repr(align(16))]
873    #[derive(Clone, Debug, PartialEq)]
874    struct Aligned(u8);
875
876    let mut x: SharedVector<Aligned> = Default::default();
877    assert!(x.is_empty());
878    for i in 0..8 {
879        x.push(Aligned(i));
880    }
881    let y = x.clone();
882    assert_eq!(x.pop(), Some(Aligned(7)));
883    x.resize(4, Aligned(0));
884    x.clear();
885    assert!(x.is_empty());
886    assert_eq!(y.len(), 8);
887    assert_eq!(
888        y.as_slice().iter().map(|a| a.0).collect::<std::vec::Vec<_>>(),
889        std::vec![0, 1, 2, 3, 4, 5, 6, 7]
890    );
891}