Skip to main content

glib/collections/
strv.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::{ffi::c_char, fmt, marker::PhantomData, mem, ptr};
4
5use crate::{GStr, GString, GStringPtr, ffi, gobject_ffi, prelude::*, translate::*};
6
7// rustdoc-stripper-ignore-next
8/// Minimum size of the `StrV` allocation.
9const MIN_SIZE: usize = 16;
10
11// rustdoc-stripper-ignore-next
12/// `NULL`-terminated array of `NULL`-terminated strings.
13///
14/// The underlying memory is always `NULL`-terminated.
15///
16/// This can be used like a `&[&str]`, `&mut [&str]` and `Vec<&str>`.
17pub struct StrV {
18    ptr: ptr::NonNull<*mut c_char>,
19    // rustdoc-stripper-ignore-next
20    /// Length without the `NULL`-terminator.
21    len: usize,
22    // rustdoc-stripper-ignore-next
23    /// Capacity **with** the `NULL`-terminator, i.e. the actual allocation size.
24    capacity: usize,
25}
26
27impl fmt::Debug for StrV {
28    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29        self.as_slice().fmt(f)
30    }
31}
32
33unsafe impl Send for StrV {}
34
35unsafe impl Sync for StrV {}
36
37impl PartialEq for StrV {
38    #[inline]
39    fn eq(&self, other: &Self) -> bool {
40        self.as_slice() == other.as_slice()
41    }
42}
43
44impl Eq for StrV {}
45
46impl PartialOrd for StrV {
47    #[inline]
48    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
49        Some(self.cmp(other))
50    }
51}
52
53impl Ord for StrV {
54    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
55        self.as_slice().cmp(other.as_slice())
56    }
57}
58
59impl std::hash::Hash for StrV {
60    #[inline]
61    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
62        self.as_slice().hash(state)
63    }
64}
65
66impl PartialEq<[&'_ str]> for StrV {
67    fn eq(&self, other: &[&'_ str]) -> bool {
68        if self.len() != other.len() {
69            return false;
70        }
71
72        for (a, b) in Iterator::zip(self.iter(), other.iter()) {
73            if a != b {
74                return false;
75            }
76        }
77
78        true
79    }
80}
81
82impl PartialEq<StrV> for [&'_ str] {
83    #[inline]
84    fn eq(&self, other: &StrV) -> bool {
85        other.eq(self)
86    }
87}
88
89impl Drop for StrV {
90    #[inline]
91    fn drop(&mut self) {
92        unsafe {
93            if self.capacity != 0 {
94                ffi::g_strfreev(self.ptr.as_ptr());
95            }
96        }
97    }
98}
99
100impl Default for StrV {
101    #[inline]
102    fn default() -> Self {
103        Self::new()
104    }
105}
106
107impl AsRef<[GStringPtr]> for StrV {
108    #[inline]
109    fn as_ref(&self) -> &[GStringPtr] {
110        self.as_slice()
111    }
112}
113
114impl std::borrow::Borrow<[GStringPtr]> for StrV {
115    #[inline]
116    fn borrow(&self) -> &[GStringPtr] {
117        self.as_slice()
118    }
119}
120
121impl AsRef<StrVRef> for StrV {
122    #[inline]
123    fn as_ref(&self) -> &StrVRef {
124        self.into()
125    }
126}
127
128impl std::borrow::Borrow<StrVRef> for StrV {
129    #[inline]
130    fn borrow(&self) -> &StrVRef {
131        self.into()
132    }
133}
134
135impl std::ops::Deref for StrV {
136    type Target = StrVRef;
137
138    #[inline]
139    fn deref(&self) -> &StrVRef {
140        self.into()
141    }
142}
143
144impl std::iter::Extend<GString> for StrV {
145    #[inline]
146    fn extend<I: IntoIterator<Item = GString>>(&mut self, iter: I) {
147        let iter = iter.into_iter();
148        self.reserve(iter.size_hint().0);
149
150        for item in iter {
151            self.push(item);
152        }
153    }
154}
155
156impl<'a> std::iter::Extend<&'a str> for StrV {
157    #[inline]
158    fn extend<I: IntoIterator<Item = &'a str>>(&mut self, iter: I) {
159        let iter = iter.into_iter();
160        self.reserve(iter.size_hint().0);
161
162        for item in iter {
163            self.push(GString::from(item));
164        }
165    }
166}
167
168impl std::iter::FromIterator<GString> for StrV {
169    #[inline]
170    fn from_iter<I: IntoIterator<Item = GString>>(iter: I) -> Self {
171        let iter = iter.into_iter();
172        let mut s = Self::with_capacity(iter.size_hint().0);
173        for item in iter {
174            s.push(item);
175        }
176        s
177    }
178}
179
180impl<'a> std::iter::IntoIterator for &'a StrV {
181    type Item = &'a GStringPtr;
182    type IntoIter = std::slice::Iter<'a, GStringPtr>;
183
184    #[inline]
185    fn into_iter(self) -> Self::IntoIter {
186        self.as_slice().iter()
187    }
188}
189
190impl std::iter::IntoIterator for StrV {
191    type Item = GString;
192    type IntoIter = IntoIter;
193
194    #[inline]
195    fn into_iter(self) -> Self::IntoIter {
196        IntoIter::new(self)
197    }
198}
199
200pub struct IntoIter {
201    ptr: ptr::NonNull<*mut c_char>,
202    idx: ptr::NonNull<*mut c_char>,
203    len: usize,
204    empty: bool,
205}
206
207impl IntoIter {
208    #[inline]
209    fn new(slice: StrV) -> Self {
210        let slice = mem::ManuallyDrop::new(slice);
211        IntoIter {
212            ptr: slice.ptr,
213            idx: slice.ptr,
214            len: slice.len,
215            empty: slice.capacity == 0,
216        }
217    }
218
219    // rustdoc-stripper-ignore-next
220    /// Returns the remaining items as slice.
221    #[inline]
222    pub const fn as_slice(&self) -> &[GStringPtr] {
223        unsafe {
224            if self.len == 0 {
225                &[]
226            } else {
227                std::slice::from_raw_parts(self.idx.as_ptr() as *const GStringPtr, self.len)
228            }
229        }
230    }
231}
232
233impl Drop for IntoIter {
234    #[inline]
235    fn drop(&mut self) {
236        unsafe {
237            for i in 0..self.len {
238                ffi::g_free(*self.idx.as_ptr().add(i) as ffi::gpointer);
239            }
240
241            if !self.empty {
242                ffi::g_free(self.ptr.as_ptr() as ffi::gpointer);
243            }
244        }
245    }
246}
247
248impl Iterator for IntoIter {
249    type Item = GString;
250
251    #[inline]
252    fn next(&mut self) -> Option<Self::Item> {
253        if self.len == 0 {
254            return None;
255        }
256
257        unsafe {
258            let p = self.idx.as_ptr();
259            self.len -= 1;
260            self.idx = ptr::NonNull::new_unchecked(p.add(1));
261            Some(GString::from_glib_full(*p))
262        }
263    }
264
265    #[inline]
266    fn size_hint(&self) -> (usize, Option<usize>) {
267        (self.len, Some(self.len))
268    }
269
270    #[inline]
271    fn count(self) -> usize {
272        self.len
273    }
274
275    #[inline]
276    fn last(mut self) -> Option<GString> {
277        if self.len == 0 {
278            None
279        } else {
280            self.len -= 1;
281            Some(unsafe { GString::from_glib_full(*self.idx.as_ptr().add(self.len)) })
282        }
283    }
284}
285
286impl DoubleEndedIterator for IntoIter {
287    #[inline]
288    fn next_back(&mut self) -> Option<GString> {
289        if self.len == 0 {
290            None
291        } else {
292            self.len -= 1;
293            Some(unsafe { GString::from_glib_full(*self.idx.as_ptr().add(self.len)) })
294        }
295    }
296}
297
298impl ExactSizeIterator for IntoIter {}
299
300impl std::iter::FusedIterator for IntoIter {}
301
302impl From<StrV> for Vec<GString> {
303    #[inline]
304    fn from(value: StrV) -> Self {
305        value.into_iter().collect()
306    }
307}
308
309impl From<Vec<String>> for StrV {
310    #[inline]
311    fn from(value: Vec<String>) -> Self {
312        unsafe {
313            let len = value.len();
314            let mut s = Self::with_capacity(len);
315            for (i, item) in value.into_iter().enumerate() {
316                *s.ptr.as_ptr().add(i) = GString::from(item).into_glib_ptr();
317            }
318            s.len = len;
319            *s.ptr.as_ptr().add(s.len) = ptr::null_mut();
320            s
321        }
322    }
323}
324
325impl From<Vec<&'_ str>> for StrV {
326    #[inline]
327    fn from(value: Vec<&'_ str>) -> Self {
328        value.as_slice().into()
329    }
330}
331
332impl From<Vec<GString>> for StrV {
333    #[inline]
334    fn from(value: Vec<GString>) -> Self {
335        unsafe {
336            let len = value.len();
337            let mut s = Self::with_capacity(len);
338            for (i, v) in value.into_iter().enumerate() {
339                *s.ptr.as_ptr().add(i) = v.into_glib_ptr();
340            }
341            s.len = len;
342            *s.ptr.as_ptr().add(s.len) = ptr::null_mut();
343            s
344        }
345    }
346}
347
348impl<const N: usize> From<[GString; N]> for StrV {
349    #[inline]
350    fn from(value: [GString; N]) -> Self {
351        unsafe {
352            let len = value.len();
353            let mut s = Self::with_capacity(len);
354            for (i, v) in value.into_iter().enumerate() {
355                *s.ptr.as_ptr().add(i) = v.into_glib_ptr();
356            }
357            s.len = len;
358            *s.ptr.as_ptr().add(s.len) = ptr::null_mut();
359            s
360        }
361    }
362}
363
364impl<const N: usize> From<[String; N]> for StrV {
365    #[inline]
366    fn from(value: [String; N]) -> Self {
367        unsafe {
368            let len = value.len();
369            let mut s = Self::with_capacity(len);
370            for (i, v) in value.into_iter().enumerate() {
371                *s.ptr.as_ptr().add(i) = GString::from(v).into_glib_ptr();
372            }
373            s.len = len;
374            *s.ptr.as_ptr().add(s.len) = ptr::null_mut();
375            s
376        }
377    }
378}
379
380impl<const N: usize> From<[&'_ str; N]> for StrV {
381    #[inline]
382    fn from(value: [&'_ str; N]) -> Self {
383        unsafe {
384            let mut s = Self::with_capacity(value.len());
385            for (i, item) in value.iter().enumerate() {
386                *s.ptr.as_ptr().add(i) = GString::from(*item).into_glib_ptr();
387            }
388            s.len = value.len();
389            *s.ptr.as_ptr().add(s.len) = ptr::null_mut();
390            s
391        }
392    }
393}
394
395impl<const N: usize> From<[&'_ GStr; N]> for StrV {
396    #[inline]
397    fn from(value: [&'_ GStr; N]) -> Self {
398        unsafe {
399            let mut s = Self::with_capacity(value.len());
400            for (i, item) in value.iter().enumerate() {
401                *s.ptr.as_ptr().add(i) = GString::from(*item).into_glib_ptr();
402            }
403            s.len = value.len();
404            *s.ptr.as_ptr().add(s.len) = ptr::null_mut();
405            s
406        }
407    }
408}
409
410impl From<&'_ [&'_ str]> for StrV {
411    #[inline]
412    fn from(value: &'_ [&'_ str]) -> Self {
413        unsafe {
414            let mut s = Self::with_capacity(value.len());
415            for (i, item) in value.iter().enumerate() {
416                *s.ptr.as_ptr().add(i) = GString::from(*item).into_glib_ptr();
417            }
418            s.len = value.len();
419            *s.ptr.as_ptr().add(s.len) = ptr::null_mut();
420            s
421        }
422    }
423}
424
425impl From<&'_ [&'_ GStr]> for StrV {
426    #[inline]
427    fn from(value: &'_ [&'_ GStr]) -> Self {
428        unsafe {
429            let mut s = Self::with_capacity(value.len());
430            for (i, item) in value.iter().enumerate() {
431                *s.ptr.as_ptr().add(i) = GString::from(*item).into_glib_ptr();
432            }
433            s.len = value.len();
434            *s.ptr.as_ptr().add(s.len) = ptr::null_mut();
435            s
436        }
437    }
438}
439
440impl From<crate::PtrSlice<GStringPtr>> for StrV {
441    #[inline]
442    fn from(value: crate::PtrSlice<GStringPtr>) -> Self {
443        let len = value.len();
444        unsafe { Self::from_glib_full_num(value.into_glib_ptr(), len, true) }
445    }
446}
447
448impl From<StrV> for crate::PtrSlice<GStringPtr> {
449    #[inline]
450    fn from(value: StrV) -> Self {
451        let len = value.len();
452        unsafe { Self::from_glib_full_num(value.into_glib_ptr(), len, true) }
453    }
454}
455
456impl Clone for StrV {
457    #[inline]
458    fn clone(&self) -> Self {
459        unsafe {
460            let mut s = Self::with_capacity(self.len());
461            for (i, item) in self.iter().enumerate() {
462                *s.ptr.as_ptr().add(i) = GString::from(item.as_str()).into_glib_ptr();
463            }
464            s.len = self.len();
465            *s.ptr.as_ptr().add(s.len) = ptr::null_mut();
466            s
467        }
468    }
469}
470
471impl StrV {
472    // rustdoc-stripper-ignore-next
473    /// Borrows a C array.
474    #[inline]
475    pub unsafe fn from_glib_borrow<'a>(ptr: *const *const c_char) -> &'a [GStringPtr] {
476        unsafe {
477            let mut len = 0;
478            if !ptr.is_null() {
479                while !(*ptr.add(len)).is_null() {
480                    len += 1;
481                }
482            }
483            Self::from_glib_borrow_num(ptr, len)
484        }
485    }
486
487    // rustdoc-stripper-ignore-next
488    /// Borrows a C array.
489    #[inline]
490    pub unsafe fn from_glib_borrow_num<'a>(
491        ptr: *const *const c_char,
492        len: usize,
493    ) -> &'a [GStringPtr] {
494        unsafe {
495            debug_assert!(!ptr.is_null() || len == 0);
496
497            if len == 0 {
498                &[]
499            } else {
500                std::slice::from_raw_parts(ptr as *const GStringPtr, len)
501            }
502        }
503    }
504
505    // rustdoc-stripper-ignore-next
506    /// Create a new `StrV` around a C array.
507    #[inline]
508    pub unsafe fn from_glib_none_num(
509        ptr: *const *const c_char,
510        len: usize,
511        _null_terminated: bool,
512    ) -> Self {
513        unsafe {
514            debug_assert!(!ptr.is_null() || len == 0);
515
516            if len == 0 {
517                StrV::default()
518            } else {
519                // Allocate space for len + 1 pointers, one pointer for each string and a trailing
520                // null pointer.
521                let new_ptr =
522                    ffi::g_malloc(mem::size_of::<*mut c_char>() * (len + 1)) as *mut *mut c_char;
523
524                // Need to clone every item because we don't own it here
525                for i in 0..len {
526                    let p = ptr.add(i) as *mut *const c_char;
527                    let q = new_ptr.add(i) as *mut *const c_char;
528                    *q = ffi::g_strdup(*p);
529                }
530
531                *new_ptr.add(len) = ptr::null_mut();
532
533                StrV {
534                    ptr: ptr::NonNull::new_unchecked(new_ptr),
535                    len,
536                    capacity: len + 1,
537                }
538            }
539        }
540    }
541
542    // rustdoc-stripper-ignore-next
543    /// Create a new `StrV` around a C array.
544    #[inline]
545    pub unsafe fn from_glib_container_num(
546        ptr: *mut *const c_char,
547        len: usize,
548        null_terminated: bool,
549    ) -> Self {
550        unsafe {
551            debug_assert!(!ptr.is_null() || len == 0);
552
553            if len == 0 {
554                ffi::g_free(ptr as ffi::gpointer);
555                StrV::default()
556            } else {
557                // Need to clone every item because we don't own it here
558                for i in 0..len {
559                    let p = ptr.add(i);
560                    *p = ffi::g_strdup(*p);
561                }
562
563                // And now it can be handled exactly the same as `from_glib_full_num()`.
564                Self::from_glib_full_num(ptr as *mut *mut c_char, len, null_terminated)
565            }
566        }
567    }
568
569    // rustdoc-stripper-ignore-next
570    /// Create a new `StrV` around a C array.
571    #[inline]
572    pub unsafe fn from_glib_full_num(
573        ptr: *mut *mut c_char,
574        len: usize,
575        null_terminated: bool,
576    ) -> Self {
577        unsafe {
578            debug_assert!(!ptr.is_null() || len == 0);
579
580            if len == 0 {
581                ffi::g_free(ptr as ffi::gpointer);
582                StrV::default()
583            } else {
584                if null_terminated {
585                    return StrV {
586                        ptr: ptr::NonNull::new_unchecked(ptr),
587                        len,
588                        capacity: len + 1,
589                    };
590                }
591
592                // Need to re-allocate here for adding the NULL-terminator
593                let capacity = len + 1;
594                assert_ne!(capacity, 0);
595                let ptr = ffi::g_realloc(
596                    ptr as *mut _,
597                    mem::size_of::<*mut c_char>().checked_mul(capacity).unwrap(),
598                ) as *mut *mut c_char;
599                *ptr.add(len) = ptr::null_mut();
600
601                StrV {
602                    ptr: ptr::NonNull::new_unchecked(ptr),
603                    len,
604                    capacity,
605                }
606            }
607        }
608    }
609
610    // rustdoc-stripper-ignore-next
611    /// Create a new `StrV` around a `NULL`-terminated C array.
612    #[inline]
613    pub unsafe fn from_glib_none(ptr: *const *const c_char) -> Self {
614        unsafe {
615            let mut len = 0;
616            if !ptr.is_null() {
617                while !(*ptr.add(len)).is_null() {
618                    len += 1;
619                }
620            }
621
622            StrV::from_glib_none_num(ptr, len, true)
623        }
624    }
625
626    // rustdoc-stripper-ignore-next
627    /// Create a new `StrV` around a `NULL`-terminated C array.
628    #[inline]
629    pub unsafe fn from_glib_container(ptr: *mut *const c_char) -> Self {
630        unsafe {
631            let mut len = 0;
632            if !ptr.is_null() {
633                while !(*ptr.add(len)).is_null() {
634                    len += 1;
635                }
636            }
637
638            StrV::from_glib_container_num(ptr, len, true)
639        }
640    }
641
642    // rustdoc-stripper-ignore-next
643    /// Create a new `StrV` around a `NULL`-terminated C array.
644    #[inline]
645    pub unsafe fn from_glib_full(ptr: *mut *mut c_char) -> Self {
646        unsafe {
647            let mut len = 0;
648            if !ptr.is_null() {
649                while !(*ptr.add(len)).is_null() {
650                    len += 1;
651                }
652            }
653
654            StrV::from_glib_full_num(ptr, len, true)
655        }
656    }
657
658    // rustdoc-stripper-ignore-next
659    /// Creates a new empty slice.
660    #[inline]
661    pub fn new() -> Self {
662        StrV {
663            ptr: ptr::NonNull::dangling(),
664            len: 0,
665            capacity: 0,
666        }
667    }
668
669    // rustdoc-stripper-ignore-next
670    /// Creates a new empty slice with the given capacity.
671    #[inline]
672    pub fn with_capacity(capacity: usize) -> Self {
673        let mut s = Self::new();
674        s.reserve(capacity);
675        s
676    }
677
678    // rustdoc-stripper-ignore-next
679    /// Returns the underlying pointer.
680    ///
681    /// This is guaranteed to be `NULL`-terminated.
682    #[inline]
683    pub fn as_ptr(&self) -> *const *mut c_char {
684        if self.len == 0 {
685            static EMPTY: [usize; 1] = [0];
686
687            EMPTY.as_ptr() as *const _
688        } else {
689            self.ptr.as_ptr()
690        }
691    }
692
693    // rustdoc-stripper-ignore-next
694    /// Consumes the slice and returns the underlying pointer.
695    ///
696    /// This is guaranteed to be `NULL`-terminated.
697    #[inline]
698    pub fn into_raw(mut self) -> *mut *mut c_char {
699        // Make sure to allocate a valid pointer that points to a
700        // NULL-pointer.
701        if self.len == 0 {
702            self.reserve(0);
703            unsafe {
704                *self.ptr.as_ptr().add(0) = ptr::null_mut();
705            }
706        }
707
708        self.len = 0;
709        self.capacity = 0;
710        self.ptr.as_ptr()
711    }
712
713    // rustdoc-stripper-ignore-next
714    /// Gets the length of the slice.
715    #[inline]
716    pub fn len(&self) -> usize {
717        self.len
718    }
719
720    // rustdoc-stripper-ignore-next
721    /// Returns `true` if the slice is empty.
722    #[inline]
723    pub fn is_empty(&self) -> bool {
724        self.len == 0
725    }
726
727    // rustdoc-stripper-ignore-next
728    /// Returns the capacity of the slice.
729    ///
730    /// This includes the space that is reserved for the `NULL`-terminator.
731    #[inline]
732    pub fn capacity(&self) -> usize {
733        self.capacity
734    }
735
736    // rustdoc-stripper-ignore-next
737    /// Sets the length of the slice to `len`.
738    ///
739    /// # SAFETY
740    ///
741    /// There must be at least `len` valid items and a `NULL`-terminator after the last item.
742    pub unsafe fn set_len(&mut self, len: usize) {
743        self.len = len;
744    }
745
746    // rustdoc-stripper-ignore-next
747    /// Reserves at least this much additional capacity.
748    #[allow(clippy::int_plus_one)]
749    pub fn reserve(&mut self, additional: usize) {
750        // Nothing new to reserve as there's still enough space
751        if additional < self.capacity - self.len {
752            return;
753        }
754
755        let new_capacity =
756            usize::next_power_of_two(std::cmp::max(self.len + additional, MIN_SIZE) + 1);
757        assert_ne!(new_capacity, 0);
758        assert!(new_capacity > self.capacity);
759
760        unsafe {
761            let ptr = if self.capacity == 0 {
762                ptr::null_mut()
763            } else {
764                self.ptr.as_ptr() as *mut _
765            };
766            let new_ptr = ffi::g_realloc(
767                ptr,
768                mem::size_of::<*mut c_char>()
769                    .checked_mul(new_capacity)
770                    .unwrap(),
771            ) as *mut *mut c_char;
772            if self.capacity == 0 {
773                *new_ptr = ptr::null_mut();
774            }
775            self.ptr = ptr::NonNull::new_unchecked(new_ptr);
776            self.capacity = new_capacity;
777        }
778    }
779
780    // rustdoc-stripper-ignore-next
781    /// Borrows this slice as a `&[GStringPtr]`.
782    #[inline]
783    pub const fn as_slice(&self) -> &[GStringPtr] {
784        unsafe {
785            if self.len == 0 {
786                &[]
787            } else {
788                std::slice::from_raw_parts(self.ptr.as_ptr() as *const GStringPtr, self.len)
789            }
790        }
791    }
792
793    // rustdoc-stripper-ignore-next
794    /// Removes all items from the slice.
795    #[inline]
796    pub fn clear(&mut self) {
797        unsafe {
798            for i in 0..self.len {
799                ffi::g_free(*self.ptr.as_ptr().add(i) as ffi::gpointer);
800            }
801
802            if self.capacity != 0 {
803                *self.ptr.as_ptr().add(0) = ptr::null_mut();
804            }
805
806            self.len = 0;
807        }
808    }
809
810    // rustdoc-stripper-ignore-next
811    /// Clones and appends all elements in `slice` to the slice.
812    #[inline]
813    pub fn extend_from_slice<S: AsRef<str>>(&mut self, other: &[S]) {
814        // Nothing new to reserve as there's still enough space
815        if other.len() >= self.capacity - self.len {
816            self.reserve(other.len());
817        }
818
819        unsafe {
820            for item in other {
821                *self.ptr.as_ptr().add(self.len) = GString::from(item.as_ref()).into_glib_ptr();
822                self.len += 1;
823
824                // Add null terminator on every iteration because `as_ref`
825                // may panic
826                *self.ptr.as_ptr().add(self.len) = ptr::null_mut();
827            }
828        }
829    }
830
831    // rustdoc-stripper-ignore-next
832    /// Inserts `item` at position `index` of the slice, shifting all elements after it to the
833    /// right.
834    #[inline]
835    pub fn insert(&mut self, index: usize, item: GString) {
836        assert!(index <= self.len);
837
838        // Nothing new to reserve as there's still enough space
839        if 1 >= self.capacity - self.len {
840            self.reserve(1);
841        }
842
843        unsafe {
844            if index == self.len {
845                *self.ptr.as_ptr().add(self.len) = item.into_glib_ptr();
846            } else {
847                let p = self.ptr.as_ptr().add(index);
848                ptr::copy(p, p.add(1), self.len - index);
849                *self.ptr.as_ptr().add(index) = item.into_glib_ptr();
850            }
851
852            self.len += 1;
853
854            *self.ptr.as_ptr().add(self.len) = ptr::null_mut();
855        }
856    }
857
858    // rustdoc-stripper-ignore-next
859    /// Pushes `item` to the end of the slice.
860    #[inline]
861    pub fn push(&mut self, item: GString) {
862        // Nothing new to reserve as there's still enough space
863        if 1 >= self.capacity - self.len {
864            self.reserve(1);
865        }
866
867        unsafe {
868            *self.ptr.as_ptr().add(self.len) = item.into_glib_ptr();
869            self.len += 1;
870
871            *self.ptr.as_ptr().add(self.len) = ptr::null_mut();
872        }
873    }
874
875    // rustdoc-stripper-ignore-next
876    /// Removes item from position `index` of the slice, shifting all elements after it to the
877    /// left.
878    #[inline]
879    pub fn remove(&mut self, index: usize) -> GString {
880        assert!(index < self.len);
881
882        unsafe {
883            let p = self.ptr.as_ptr().add(index);
884            let item = *p;
885            ptr::copy(p.add(1), p, self.len - index - 1);
886
887            self.len -= 1;
888
889            *self.ptr.as_ptr().add(self.len) = ptr::null_mut();
890
891            GString::from_glib_full(item)
892        }
893    }
894
895    // rustdoc-stripper-ignore-next
896    /// Swaps item from position `index` of the slice and returns it.
897    #[inline]
898    pub fn swap(&mut self, index: usize, new_item: GString) -> GString {
899        assert!(index < self.len);
900
901        unsafe {
902            let p = self.ptr.as_ptr().add(index);
903            let item = *p;
904            *p = new_item.into_glib_ptr();
905
906            GString::from_glib_full(item)
907        }
908    }
909
910    // rustdoc-stripper-ignore-next
911    /// Removes the last item of the slice and returns it.
912    #[inline]
913    pub fn pop(&mut self) -> Option<GString> {
914        if self.len == 0 {
915            return None;
916        }
917
918        unsafe {
919            self.len -= 1;
920            let p = self.ptr.as_ptr().add(self.len);
921            let item = *p;
922
923            *self.ptr.as_ptr().add(self.len) = ptr::null_mut();
924
925            Some(GString::from_glib_full(item))
926        }
927    }
928
929    // rustdoc-stripper-ignore-next
930    /// Shortens the slice by keeping the last `len` items.
931    ///
932    /// If there are fewer than `len` items then this has no effect.
933    #[inline]
934    pub fn truncate(&mut self, len: usize) {
935        if self.len <= len {
936            return;
937        }
938
939        unsafe {
940            while self.len > len {
941                self.len -= 1;
942                let p = self.ptr.as_ptr().add(self.len);
943                ffi::g_free(*p as ffi::gpointer);
944                *p = ptr::null_mut();
945            }
946        }
947    }
948
949    // rustdoc-stripper-ignore-next
950    /// Joins the strings into a longer string, with an optional separator
951    #[inline]
952    #[doc(alias = "g_strjoinv")]
953    pub fn join(&self, separator: Option<impl IntoGStr>) -> GString {
954        separator.run_with_gstr(|separator| unsafe {
955            from_glib_full(ffi::g_strjoinv(
956                separator.to_glib_none().0,
957                self.as_ptr() as *mut _,
958            ))
959        })
960    }
961
962    // rustdoc-stripper-ignore-next
963    /// Checks whether the `StrV` contains the specified string
964    #[inline]
965    #[doc(alias = "g_strv_contains")]
966    pub fn contains(&self, s: impl IntoGStr) -> bool {
967        s.run_with_gstr(|s| unsafe {
968            from_glib(ffi::g_strv_contains(
969                self.as_ptr() as *const _,
970                s.to_glib_none().0,
971            ))
972        })
973    }
974}
975
976impl FromGlibContainer<*mut c_char, *mut *mut c_char> for StrV {
977    #[inline]
978    unsafe fn from_glib_none_num(ptr: *mut *mut c_char, num: usize) -> Self {
979        unsafe { Self::from_glib_none_num(ptr as *const *const c_char, num, false) }
980    }
981
982    #[inline]
983    unsafe fn from_glib_container_num(ptr: *mut *mut c_char, num: usize) -> Self {
984        unsafe { Self::from_glib_container_num(ptr as *mut *const c_char, num, false) }
985    }
986
987    #[inline]
988    unsafe fn from_glib_full_num(ptr: *mut *mut c_char, num: usize) -> Self {
989        unsafe { Self::from_glib_full_num(ptr, num, false) }
990    }
991}
992
993impl FromGlibContainer<*mut c_char, *const *mut c_char> for StrV {
994    unsafe fn from_glib_none_num(ptr: *const *mut c_char, num: usize) -> Self {
995        unsafe { Self::from_glib_none_num(ptr as *const *const c_char, num, false) }
996    }
997
998    unsafe fn from_glib_container_num(_ptr: *const *mut c_char, _num: usize) -> Self {
999        unimplemented!();
1000    }
1001
1002    unsafe fn from_glib_full_num(_ptr: *const *mut c_char, _num: usize) -> Self {
1003        unimplemented!();
1004    }
1005}
1006
1007impl FromGlibPtrContainer<*mut c_char, *mut *mut c_char> for StrV {
1008    #[inline]
1009    unsafe fn from_glib_none(ptr: *mut *mut c_char) -> Self {
1010        unsafe { Self::from_glib_none(ptr as *const *const c_char) }
1011    }
1012
1013    #[inline]
1014    unsafe fn from_glib_container(ptr: *mut *mut c_char) -> Self {
1015        unsafe { Self::from_glib_container(ptr as *mut *const c_char) }
1016    }
1017
1018    #[inline]
1019    unsafe fn from_glib_full(ptr: *mut *mut c_char) -> Self {
1020        unsafe { Self::from_glib_full(ptr) }
1021    }
1022}
1023
1024impl FromGlibPtrContainer<*mut c_char, *const *mut c_char> for StrV {
1025    #[inline]
1026    unsafe fn from_glib_none(ptr: *const *mut c_char) -> Self {
1027        unsafe { Self::from_glib_none(ptr as *const *const c_char) }
1028    }
1029
1030    unsafe fn from_glib_container(_ptr: *const *mut c_char) -> Self {
1031        unimplemented!();
1032    }
1033
1034    unsafe fn from_glib_full(_ptr: *const *mut c_char) -> Self {
1035        unimplemented!();
1036    }
1037}
1038
1039impl<'a> ToGlibPtr<'a, *mut *mut c_char> for StrV {
1040    type Storage = PhantomData<&'a Self>;
1041
1042    #[inline]
1043    fn to_glib_none(&'a self) -> Stash<'a, *mut *mut c_char, Self> {
1044        Stash(self.as_ptr() as *mut _, PhantomData)
1045    }
1046
1047    #[inline]
1048    fn to_glib_container(&'a self) -> Stash<'a, *mut *mut c_char, Self> {
1049        unsafe {
1050            let ptr =
1051                ffi::g_malloc(mem::size_of::<*mut c_char>() * (self.len() + 1)) as *mut *mut c_char;
1052            ptr::copy_nonoverlapping(self.as_ptr(), ptr, self.len() + 1);
1053            Stash(ptr, PhantomData)
1054        }
1055    }
1056
1057    #[inline]
1058    fn to_glib_full(&self) -> *mut *mut c_char {
1059        self.clone().into_raw()
1060    }
1061}
1062
1063impl<'a> ToGlibPtr<'a, *const *mut c_char> for StrV {
1064    type Storage = PhantomData<&'a Self>;
1065
1066    #[inline]
1067    fn to_glib_none(&'a self) -> Stash<'a, *const *mut c_char, Self> {
1068        Stash(self.as_ptr(), PhantomData)
1069    }
1070}
1071
1072impl IntoGlibPtr<*mut *mut c_char> for StrV {
1073    #[inline]
1074    fn into_glib_ptr(self) -> *mut *mut c_char {
1075        self.into_raw()
1076    }
1077}
1078
1079impl StaticType for StrV {
1080    #[inline]
1081    fn static_type() -> crate::Type {
1082        <Vec<String>>::static_type()
1083    }
1084}
1085
1086impl StaticType for &'_ [GStringPtr] {
1087    #[inline]
1088    fn static_type() -> crate::Type {
1089        <Vec<String>>::static_type()
1090    }
1091}
1092
1093impl crate::value::ValueType for StrV {
1094    type Type = Vec<String>;
1095}
1096
1097unsafe impl<'a> crate::value::FromValue<'a> for StrV {
1098    type Checker = crate::value::GenericValueTypeChecker<Self>;
1099
1100    unsafe fn from_value(value: &'a crate::value::Value) -> Self {
1101        unsafe {
1102            let ptr = gobject_ffi::g_value_dup_boxed(value.to_glib_none().0) as *mut *mut c_char;
1103            FromGlibPtrContainer::from_glib_full(ptr)
1104        }
1105    }
1106}
1107
1108unsafe impl<'a> crate::value::FromValue<'a> for &'a [GStringPtr] {
1109    type Checker = crate::value::GenericValueTypeChecker<Self>;
1110
1111    unsafe fn from_value(value: &'a crate::value::Value) -> Self {
1112        unsafe {
1113            let ptr =
1114                gobject_ffi::g_value_get_boxed(value.to_glib_none().0) as *const *const c_char;
1115            StrV::from_glib_borrow(ptr)
1116        }
1117    }
1118}
1119
1120impl crate::value::ToValue for StrV {
1121    fn to_value(&self) -> crate::value::Value {
1122        unsafe {
1123            let mut value = crate::value::Value::for_value_type::<Self>();
1124            gobject_ffi::g_value_set_boxed(
1125                value.to_glib_none_mut().0,
1126                self.as_ptr() as ffi::gpointer,
1127            );
1128            value
1129        }
1130    }
1131
1132    fn value_type(&self) -> crate::Type {
1133        <StrV as StaticType>::static_type()
1134    }
1135}
1136
1137impl From<StrV> for crate::Value {
1138    #[inline]
1139    fn from(s: StrV) -> Self {
1140        unsafe {
1141            let mut value = crate::value::Value::for_value_type::<StrV>();
1142            gobject_ffi::g_value_take_boxed(
1143                value.to_glib_none_mut().0,
1144                s.into_raw() as ffi::gpointer,
1145            );
1146            value
1147        }
1148    }
1149}
1150
1151// rustdoc-stripper-ignore-next
1152/// A trait to accept both `&[T]` or `StrV` as an argument.
1153pub trait IntoStrV {
1154    // rustdoc-stripper-ignore-next
1155    /// Runs the given closure with a `NULL`-terminated array.
1156    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R;
1157}
1158
1159impl IntoStrV for StrV {
1160    #[inline]
1161    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1162        <&Self>::run_with_strv(&self, f)
1163    }
1164}
1165
1166impl IntoStrV for &'_ StrV {
1167    #[inline]
1168    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1169        f(unsafe { std::slice::from_raw_parts(self.as_ptr(), self.len()) })
1170    }
1171}
1172
1173// rustdoc-stripper-ignore-next
1174/// Maximum number of pointers to stack-allocate before falling back to a heap allocation.
1175///
1176/// The beginning will be used for the pointers, the remainder for the actual string content.
1177const MAX_STACK_ALLOCATION: usize = 16;
1178
1179impl IntoStrV for Vec<GString> {
1180    #[inline]
1181    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1182        self.as_slice().run_with_strv(f)
1183    }
1184}
1185
1186impl IntoStrV for Vec<&'_ GString> {
1187    #[inline]
1188    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1189        self.as_slice().run_with_strv(f)
1190    }
1191}
1192
1193impl IntoStrV for Vec<&'_ GStr> {
1194    #[inline]
1195    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1196        self.as_slice().run_with_strv(f)
1197    }
1198}
1199
1200impl IntoStrV for Vec<&'_ str> {
1201    #[inline]
1202    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1203        self.as_slice().run_with_strv(f)
1204    }
1205}
1206
1207impl IntoStrV for Vec<String> {
1208    #[inline]
1209    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1210        self.as_slice().run_with_strv(f)
1211    }
1212}
1213
1214impl IntoStrV for Vec<&'_ String> {
1215    #[inline]
1216    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1217        self.as_slice().run_with_strv(f)
1218    }
1219}
1220
1221impl IntoStrV for &[GString] {
1222    #[inline]
1223    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1224        let required_len = (self.len() + 1) * mem::size_of::<*mut c_char>();
1225
1226        if required_len < MAX_STACK_ALLOCATION * mem::size_of::<*mut c_char>() {
1227            unsafe {
1228                let mut s = mem::MaybeUninit::<[*mut c_char; MAX_STACK_ALLOCATION]>::uninit();
1229                let ptrs = s.as_mut_ptr() as *mut *mut c_char;
1230
1231                for (i, item) in self.iter().enumerate() {
1232                    *ptrs.add(i) = item.as_ptr() as *mut _;
1233                }
1234                *ptrs.add(self.len()) = ptr::null_mut();
1235
1236                f(std::slice::from_raw_parts(ptrs, self.len()))
1237            }
1238        } else {
1239            let mut s = StrV::with_capacity(self.len());
1240            s.extend_from_slice(self);
1241            s.run_with_strv(f)
1242        }
1243    }
1244}
1245
1246impl IntoStrV for &[&GString] {
1247    #[inline]
1248    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1249        let required_len = (self.len() + 1) * mem::size_of::<*mut c_char>();
1250
1251        if required_len < MAX_STACK_ALLOCATION * mem::size_of::<*mut c_char>() {
1252            unsafe {
1253                let mut s = mem::MaybeUninit::<[*mut c_char; MAX_STACK_ALLOCATION]>::uninit();
1254                let ptrs = s.as_mut_ptr() as *mut *mut c_char;
1255
1256                for (i, item) in self.iter().enumerate() {
1257                    *ptrs.add(i) = item.as_ptr() as *mut _;
1258                }
1259                *ptrs.add(self.len()) = ptr::null_mut();
1260
1261                f(std::slice::from_raw_parts(ptrs, self.len()))
1262            }
1263        } else {
1264            let mut s = StrV::with_capacity(self.len());
1265            s.extend_from_slice(self);
1266            s.run_with_strv(f)
1267        }
1268    }
1269}
1270
1271impl IntoStrV for &[&GStr] {
1272    #[inline]
1273    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1274        let required_len = (self.len() + 1) * mem::size_of::<*mut c_char>();
1275
1276        if required_len < MAX_STACK_ALLOCATION * mem::size_of::<*mut c_char>() {
1277            unsafe {
1278                let mut s = mem::MaybeUninit::<[*mut c_char; MAX_STACK_ALLOCATION]>::uninit();
1279                let ptrs = s.as_mut_ptr() as *mut *mut c_char;
1280
1281                for (i, item) in self.iter().enumerate() {
1282                    *ptrs.add(i) = item.as_ptr() as *mut _;
1283                }
1284                *ptrs.add(self.len()) = ptr::null_mut();
1285
1286                f(std::slice::from_raw_parts(ptrs, self.len()))
1287            }
1288        } else {
1289            let mut s = StrV::with_capacity(self.len());
1290            s.extend_from_slice(self);
1291            s.run_with_strv(f)
1292        }
1293    }
1294}
1295
1296impl IntoStrV for &[&str] {
1297    #[inline]
1298    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1299        let required_len = (self.len() + 1) * mem::size_of::<*mut c_char>()
1300            + self.iter().map(|s| s.len() + 1).sum::<usize>();
1301
1302        if required_len < MAX_STACK_ALLOCATION * mem::size_of::<*mut c_char>() {
1303            unsafe {
1304                let mut s = mem::MaybeUninit::<[*mut c_char; MAX_STACK_ALLOCATION]>::uninit();
1305                let ptrs = s.as_mut_ptr() as *mut *mut c_char;
1306                let mut strs = ptrs.add(self.len() + 1) as *mut c_char;
1307
1308                for (i, item) in self.iter().enumerate() {
1309                    ptr::copy_nonoverlapping(item.as_ptr() as *const _, strs, item.len());
1310                    *strs.add(item.len()) = 0;
1311                    *ptrs.add(i) = strs;
1312                    strs = strs.add(item.len() + 1);
1313                }
1314                *ptrs.add(self.len()) = ptr::null_mut();
1315
1316                f(std::slice::from_raw_parts(ptrs, self.len()))
1317            }
1318        } else {
1319            let mut s = StrV::with_capacity(self.len());
1320            s.extend_from_slice(self);
1321            s.run_with_strv(f)
1322        }
1323    }
1324}
1325
1326impl IntoStrV for &[String] {
1327    #[inline]
1328    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1329        let required_len = (self.len() + 1) * mem::size_of::<*mut c_char>()
1330            + self.iter().map(|s| s.len() + 1).sum::<usize>();
1331
1332        if required_len < MAX_STACK_ALLOCATION * mem::size_of::<*mut c_char>() {
1333            unsafe {
1334                let mut s = mem::MaybeUninit::<[*mut c_char; MAX_STACK_ALLOCATION]>::uninit();
1335                let ptrs = s.as_mut_ptr() as *mut *mut c_char;
1336                let mut strs = ptrs.add(self.len() + 1) as *mut c_char;
1337
1338                for (i, item) in self.iter().enumerate() {
1339                    ptr::copy_nonoverlapping(item.as_ptr() as *const _, strs, item.len());
1340                    *strs.add(item.len()) = 0;
1341                    *ptrs.add(i) = strs;
1342                    strs = strs.add(item.len() + 1);
1343                }
1344                *ptrs.add(self.len()) = ptr::null_mut();
1345
1346                f(std::slice::from_raw_parts(ptrs, self.len()))
1347            }
1348        } else {
1349            let mut s = StrV::with_capacity(self.len());
1350            s.extend_from_slice(self);
1351            s.run_with_strv(f)
1352        }
1353    }
1354}
1355
1356impl IntoStrV for &[&String] {
1357    #[inline]
1358    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1359        let required_len = (self.len() + 1) * mem::size_of::<*mut c_char>()
1360            + self.iter().map(|s| s.len() + 1).sum::<usize>();
1361
1362        if required_len < MAX_STACK_ALLOCATION * mem::size_of::<*mut c_char>() {
1363            unsafe {
1364                let mut s = mem::MaybeUninit::<[*mut c_char; MAX_STACK_ALLOCATION]>::uninit();
1365                let ptrs = s.as_mut_ptr() as *mut *mut c_char;
1366                let mut strs = ptrs.add(self.len() + 1) as *mut c_char;
1367
1368                for (i, item) in self.iter().enumerate() {
1369                    ptr::copy_nonoverlapping(item.as_ptr() as *const _, strs, item.len());
1370                    *strs.add(item.len()) = 0;
1371                    *ptrs.add(i) = strs;
1372                    strs = strs.add(item.len() + 1);
1373                }
1374                *ptrs.add(self.len()) = ptr::null_mut();
1375
1376                f(std::slice::from_raw_parts(ptrs, self.len()))
1377            }
1378        } else {
1379            let mut s = StrV::with_capacity(self.len());
1380            s.extend_from_slice(self);
1381            s.run_with_strv(f)
1382        }
1383    }
1384}
1385
1386impl<const N: usize> IntoStrV for [GString; N] {
1387    #[inline]
1388    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1389        self.as_slice().run_with_strv(f)
1390    }
1391}
1392
1393impl<const N: usize> IntoStrV for [&'_ GString; N] {
1394    #[inline]
1395    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1396        self.as_slice().run_with_strv(f)
1397    }
1398}
1399
1400impl<const N: usize> IntoStrV for [&'_ GStr; N] {
1401    #[inline]
1402    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1403        self.as_slice().run_with_strv(f)
1404    }
1405}
1406
1407impl<const N: usize> IntoStrV for [&'_ str; N] {
1408    #[inline]
1409    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1410        self.as_slice().run_with_strv(f)
1411    }
1412}
1413
1414impl<const N: usize> IntoStrV for [String; N] {
1415    #[inline]
1416    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1417        self.as_slice().run_with_strv(f)
1418    }
1419}
1420
1421impl<const N: usize> IntoStrV for [&'_ String; N] {
1422    #[inline]
1423    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1424        self.as_slice().run_with_strv(f)
1425    }
1426}
1427
1428// rustdoc-stripper-ignore-next
1429/// Representation of a borrowed `NULL`-terminated C array of `NULL`-terminated UTF-8 strings.
1430///
1431/// It can be constructed safely from a `&StrV` and unsafely from a pointer to a C array.
1432/// This type is very similar to `[GStringPtr]`, but with one added constraint: the underlying C array must be `NULL`-terminated.
1433#[repr(transparent)]
1434pub struct StrVRef {
1435    inner: [GStringPtr],
1436}
1437
1438impl StrVRef {
1439    // rustdoc-stripper-ignore-next
1440    /// Borrows a C array.
1441    /// # Safety
1442    ///
1443    /// The provided pointer **must** be `NULL`-terminated. It is undefined behavior to
1444    /// pass a pointer that does not uphold this condition.
1445    #[inline]
1446    pub unsafe fn from_glib_borrow<'a>(ptr: *const *const c_char) -> &'a StrVRef {
1447        unsafe {
1448            let slice = StrV::from_glib_borrow(ptr);
1449            &*(slice as *const [GStringPtr] as *const StrVRef)
1450        }
1451    }
1452
1453    // rustdoc-stripper-ignore-next
1454    /// Borrows a C array.
1455    /// # Safety
1456    ///
1457    /// The provided pointer **must** be `NULL`-terminated. It is undefined behavior to
1458    /// pass a pointer that does not uphold this condition.
1459    #[inline]
1460    pub unsafe fn from_glib_borrow_num<'a>(ptr: *const *const c_char, len: usize) -> &'a StrVRef {
1461        unsafe {
1462            let slice = StrV::from_glib_borrow_num(ptr, len);
1463            &*(slice as *const [GStringPtr] as *const StrVRef)
1464        }
1465    }
1466
1467    // rustdoc-stripper-ignore-next
1468    /// Returns the underlying pointer.
1469    ///
1470    /// This is guaranteed to be nul-terminated.
1471    #[inline]
1472    pub const fn as_ptr(&self) -> *const *const c_char {
1473        self.inner.as_ptr() as *const *const _
1474    }
1475}
1476
1477impl fmt::Debug for StrVRef {
1478    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1479        self.inner.fmt(f)
1480    }
1481}
1482
1483unsafe impl Send for StrVRef {}
1484
1485unsafe impl Sync for StrVRef {}
1486
1487impl PartialEq for StrVRef {
1488    #[inline]
1489    fn eq(&self, other: &Self) -> bool {
1490        self.inner == other.inner
1491    }
1492}
1493
1494impl Eq for StrVRef {}
1495
1496impl PartialOrd for StrVRef {
1497    #[inline]
1498    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
1499        Some(self.cmp(other))
1500    }
1501}
1502
1503impl Ord for StrVRef {
1504    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
1505        self.inner.cmp(&other.inner)
1506    }
1507}
1508
1509impl std::hash::Hash for StrVRef {
1510    #[inline]
1511    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1512        self.inner.hash(state)
1513    }
1514}
1515
1516impl PartialEq<[&'_ str]> for StrVRef {
1517    fn eq(&self, other: &[&'_ str]) -> bool {
1518        if self.len() != other.len() {
1519            return false;
1520        }
1521
1522        for (a, b) in Iterator::zip(self.iter(), other.iter()) {
1523            if a != b {
1524                return false;
1525            }
1526        }
1527
1528        true
1529    }
1530}
1531
1532impl PartialEq<StrVRef> for [&'_ str] {
1533    #[inline]
1534    fn eq(&self, other: &StrVRef) -> bool {
1535        other.eq(self)
1536    }
1537}
1538
1539impl Default for &StrVRef {
1540    #[inline]
1541    fn default() -> Self {
1542        const SLICE: &[*const c_char] = &[ptr::null()];
1543        // SAFETY: `SLICE` is indeed a valid nul-terminated array.
1544        unsafe { StrVRef::from_glib_borrow(SLICE.as_ptr()) }
1545    }
1546}
1547
1548impl std::ops::Deref for StrVRef {
1549    type Target = [GStringPtr];
1550
1551    #[inline]
1552    fn deref(&self) -> &[GStringPtr] {
1553        &self.inner
1554    }
1555}
1556
1557impl<'a> std::iter::IntoIterator for &'a StrVRef {
1558    type Item = &'a GStringPtr;
1559    type IntoIter = std::slice::Iter<'a, GStringPtr>;
1560
1561    #[inline]
1562    fn into_iter(self) -> Self::IntoIter {
1563        self.inner.iter()
1564    }
1565}
1566
1567impl<'a> From<&'a StrV> for &'a StrVRef {
1568    fn from(value: &'a StrV) -> Self {
1569        let slice = value.as_slice();
1570        // Safety: `&StrV` is a null-terminated C array of nul-terminated UTF-8 strings,
1571        // therefore `&StrV::as_slice()` return a a null-terminated slice of nul-terminated UTF-8 strings,
1572        // thus it is safe to convert it to `&CStr`.
1573        unsafe { &*(slice as *const [GStringPtr] as *const StrVRef) }
1574    }
1575}
1576
1577impl FromGlibContainer<*mut c_char, *const *const c_char> for &StrVRef {
1578    unsafe fn from_glib_none_num(ptr: *const *const c_char, num: usize) -> Self {
1579        unsafe { StrVRef::from_glib_borrow_num(ptr, num) }
1580    }
1581
1582    unsafe fn from_glib_container_num(_ptr: *const *const c_char, _num: usize) -> Self {
1583        unimplemented!();
1584    }
1585
1586    unsafe fn from_glib_full_num(_ptr: *const *const c_char, _num: usize) -> Self {
1587        unimplemented!();
1588    }
1589}
1590
1591impl FromGlibPtrContainer<*mut c_char, *const *const c_char> for &StrVRef {
1592    #[inline]
1593    unsafe fn from_glib_none(ptr: *const *const c_char) -> Self {
1594        unsafe { StrVRef::from_glib_borrow(ptr) }
1595    }
1596
1597    unsafe fn from_glib_container(_ptr: *const *const c_char) -> Self {
1598        unimplemented!();
1599    }
1600
1601    unsafe fn from_glib_full(_ptr: *const *const c_char) -> Self {
1602        unimplemented!();
1603    }
1604}
1605
1606impl<'a> ToGlibPtr<'a, *const *const c_char> for StrVRef {
1607    type Storage = PhantomData<&'a Self>;
1608
1609    #[inline]
1610    fn to_glib_none(&'a self) -> Stash<'a, *const *const c_char, Self> {
1611        Stash(self.as_ptr(), PhantomData)
1612    }
1613}
1614
1615impl IntoGlibPtr<*const *const c_char> for &StrVRef {
1616    #[inline]
1617    fn into_glib_ptr(self) -> *const *const c_char {
1618        self.as_ptr()
1619    }
1620}
1621
1622impl StaticType for StrVRef {
1623    #[inline]
1624    fn static_type() -> crate::Type {
1625        <Vec<String>>::static_type()
1626    }
1627}
1628
1629unsafe impl<'a> crate::value::FromValue<'a> for &'a StrVRef {
1630    type Checker = crate::value::GenericValueTypeChecker<Self>;
1631
1632    unsafe fn from_value(value: &'a crate::value::Value) -> Self {
1633        unsafe {
1634            let ptr =
1635                gobject_ffi::g_value_get_boxed(value.to_glib_none().0) as *const *const c_char;
1636            StrVRef::from_glib_borrow(ptr)
1637        }
1638    }
1639}
1640
1641#[cfg(test)]
1642mod test {
1643    use super::*;
1644
1645    #[test]
1646    fn test_from_glib_full() {
1647        let items = ["str1", "str2", "str3", "str4"];
1648
1649        let slice = unsafe {
1650            let ptr = ffi::g_malloc(mem::size_of::<*mut c_char>() * 4) as *mut *mut c_char;
1651            *ptr.add(0) = items[0].to_glib_full();
1652            *ptr.add(1) = items[1].to_glib_full();
1653            *ptr.add(2) = items[2].to_glib_full();
1654            *ptr.add(3) = items[3].to_glib_full();
1655
1656            StrV::from_glib_full_num(ptr, 4, false)
1657        };
1658
1659        assert_eq!(items.len(), slice.len());
1660        for (a, b) in Iterator::zip(items.iter(), slice.iter()) {
1661            assert_eq!(a, b);
1662        }
1663    }
1664
1665    #[test]
1666    fn test_from_glib_container() {
1667        let items = [
1668            crate::gstr!("str1"),
1669            crate::gstr!("str2"),
1670            crate::gstr!("str3"),
1671            crate::gstr!("str4"),
1672        ];
1673
1674        let slice = unsafe {
1675            let ptr = ffi::g_malloc(mem::size_of::<*mut c_char>() * 4) as *mut *const c_char;
1676            *ptr.add(0) = items[0].as_ptr();
1677            *ptr.add(1) = items[1].as_ptr();
1678            *ptr.add(2) = items[2].as_ptr();
1679            *ptr.add(3) = items[3].as_ptr();
1680
1681            StrV::from_glib_container_num(ptr, 4, false)
1682        };
1683
1684        assert_eq!(items.len(), slice.len());
1685        for (a, b) in Iterator::zip(items.iter(), slice.iter()) {
1686            assert_eq!(a, b);
1687        }
1688    }
1689
1690    #[test]
1691    fn test_from_glib_none() {
1692        let items = [
1693            crate::gstr!("str1"),
1694            crate::gstr!("str2"),
1695            crate::gstr!("str3"),
1696            crate::gstr!("str4"),
1697        ];
1698
1699        let slice = unsafe {
1700            let ptr = ffi::g_malloc(mem::size_of::<*mut c_char>() * 4) as *mut *const c_char;
1701            *ptr.add(0) = items[0].as_ptr();
1702            *ptr.add(1) = items[1].as_ptr();
1703            *ptr.add(2) = items[2].as_ptr();
1704            *ptr.add(3) = items[3].as_ptr();
1705
1706            let res = StrV::from_glib_none_num(ptr, 4, false);
1707            ffi::g_free(ptr as ffi::gpointer);
1708            res
1709        };
1710
1711        assert_eq!(items.len(), slice.len());
1712        for (a, b) in Iterator::zip(items.iter(), slice.iter()) {
1713            assert_eq!(a, b);
1714        }
1715    }
1716
1717    #[test]
1718    fn test_from_slice() {
1719        let items = [
1720            crate::gstr!("str1"),
1721            crate::gstr!("str2"),
1722            crate::gstr!("str3"),
1723        ];
1724
1725        let slice1 = StrV::from(&items[..]);
1726        let slice2 = StrV::from(items);
1727        assert_eq!(slice1.len(), 3);
1728        assert_eq!(slice1, slice2);
1729    }
1730
1731    #[test]
1732    fn test_safe_api() {
1733        let items = [
1734            crate::gstr!("str1"),
1735            crate::gstr!("str2"),
1736            crate::gstr!("str3"),
1737        ];
1738
1739        let mut slice = StrV::from(&items[..]);
1740        assert_eq!(slice.len(), 3);
1741        slice.push(GString::from("str4"));
1742        assert_eq!(slice.len(), 4);
1743
1744        for (a, b) in Iterator::zip(items.iter(), slice.iter()) {
1745            assert_eq!(a, b);
1746        }
1747        assert_eq!(slice[3], "str4");
1748
1749        let vec = Vec::from(slice);
1750        assert_eq!(vec.len(), 4);
1751        for (a, b) in Iterator::zip(items.iter(), vec.iter()) {
1752            assert_eq!(a, b);
1753        }
1754        assert_eq!(vec[3], "str4");
1755
1756        let mut slice = StrV::from(vec);
1757        assert_eq!(slice.len(), 4);
1758        let e = slice.pop().unwrap();
1759        assert_eq!(e, "str4");
1760        assert_eq!(slice.len(), 3);
1761        slice.insert(2, e);
1762        assert_eq!(slice.len(), 4);
1763        assert_eq!(slice[0], "str1");
1764        assert_eq!(slice[1], "str2");
1765        assert_eq!(slice[2], "str4");
1766        assert_eq!(slice[3], "str3");
1767        let e = slice.remove(2);
1768        assert_eq!(e, "str4");
1769        assert_eq!(slice.len(), 3);
1770        slice.push(e);
1771        assert_eq!(slice.len(), 4);
1772
1773        for (a, b) in Iterator::zip(items.iter(), slice) {
1774            assert_eq!(*a, b);
1775        }
1776    }
1777
1778    #[test]
1779    fn test_into_strv() {
1780        let items = ["str1", "str2", "str3", "str4"];
1781
1782        items[..].run_with_strv(|s| unsafe {
1783            assert!((*s.as_ptr().add(4)).is_null());
1784            assert_eq!(s.len(), items.len());
1785            let s = StrV::from_glib_borrow(s.as_ptr() as *const *const c_char);
1786            assert_eq!(s, items);
1787        });
1788
1789        Vec::from(&items[..]).run_with_strv(|s| unsafe {
1790            assert!((*s.as_ptr().add(4)).is_null());
1791            assert_eq!(s.len(), items.len());
1792            let s = StrV::from_glib_borrow(s.as_ptr() as *const *const c_char);
1793            assert_eq!(s, items);
1794        });
1795
1796        StrV::from(&items[..]).run_with_strv(|s| unsafe {
1797            assert!((*s.as_ptr().add(4)).is_null());
1798            assert_eq!(s.len(), items.len());
1799            let s = StrV::from_glib_borrow(s.as_ptr() as *const *const c_char);
1800            assert_eq!(s, items);
1801        });
1802
1803        let v = items.iter().copied().map(String::from).collect::<Vec<_>>();
1804        items.run_with_strv(|s| unsafe {
1805            assert!((*s.as_ptr().add(4)).is_null());
1806            assert_eq!(s.len(), v.len());
1807            let s = StrV::from_glib_borrow(s.as_ptr() as *const *const c_char);
1808            assert_eq!(s, items);
1809        });
1810
1811        let v = items.iter().copied().map(GString::from).collect::<Vec<_>>();
1812        items.run_with_strv(|s| unsafe {
1813            assert!((*s.as_ptr().add(4)).is_null());
1814            assert_eq!(s.len(), v.len());
1815            let s = StrV::from_glib_borrow(s.as_ptr() as *const *const c_char);
1816            assert_eq!(s, items);
1817        });
1818    }
1819
1820    #[test]
1821    fn test_join() {
1822        let items = [
1823            crate::gstr!("str1"),
1824            crate::gstr!("str2"),
1825            crate::gstr!("str3"),
1826        ];
1827
1828        let strv = StrV::from(&items[..]);
1829        assert_eq!(strv.join(None::<&str>), "str1str2str3");
1830        assert_eq!(strv.join(Some(",")), "str1,str2,str3");
1831    }
1832
1833    #[test]
1834    fn test_contains() {
1835        let items = [
1836            crate::gstr!("str1"),
1837            crate::gstr!("str2"),
1838            crate::gstr!("str3"),
1839        ];
1840
1841        let strv = StrV::from(&items[..]);
1842        assert!(strv.contains("str2"));
1843        assert!(!strv.contains("str4"));
1844    }
1845
1846    #[test]
1847    #[should_panic]
1848    fn test_reserve_overflow() {
1849        let mut strv = StrV::from(&[crate::gstr!("foo"); 3][..]);
1850
1851        // An old implementation of `reserve` used the condition `self.len +
1852        // additional + 1 <= self.capacity`, which was prone to overflow
1853        strv.reserve(usize::MAX - 3);
1854    }
1855
1856    #[test]
1857    #[should_panic]
1858    fn test_extend_from_slice_overflow() {
1859        // We need a zero-sized type because only a slice of ZST can legally
1860        // contain up to `usize::MAX` elements.
1861        #[derive(Clone, Copy)]
1862        struct ImplicitStr;
1863
1864        impl AsRef<str> for ImplicitStr {
1865            fn as_ref(&self) -> &str {
1866                ""
1867            }
1868        }
1869
1870        let mut strv = StrV::from(&[crate::gstr!(""); 3][..]);
1871
1872        // An old implementation of `extend_from_slice` used the condition
1873        // `self.len + other.len() + 1 <= self.capacity`, which was prone to
1874        // overflow
1875        strv.extend_from_slice(&[ImplicitStr; usize::MAX - 3]);
1876    }
1877
1878    #[test]
1879    fn test_extend_from_slice_panic_safe() {
1880        struct MayPanic(bool);
1881
1882        impl AsRef<str> for MayPanic {
1883            fn as_ref(&self) -> &str {
1884                if self.0 {
1885                    panic!("panicking as per request");
1886                } else {
1887                    ""
1888                }
1889            }
1890        }
1891
1892        let mut strv = StrV::from(&[crate::gstr!(""); 3][..]);
1893        strv.clear();
1894
1895        // Write one element and panic while getting the second element
1896        _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
1897            strv.extend_from_slice(&[MayPanic(false), MayPanic(true)]);
1898        }));
1899
1900        // Check that it contains up to one element is null-terminated
1901        assert!(strv.len() <= 1);
1902        unsafe {
1903            for i in 0..strv.len() {
1904                assert!(!(*strv.as_ptr().add(i)).is_null());
1905            }
1906            assert!((*strv.as_ptr().add(strv.len())).is_null());
1907        }
1908    }
1909
1910    #[test]
1911    fn test_strv_ref_eq_str_slice() {
1912        let strv = StrV::from(&[crate::gstr!("a")][..]);
1913        let strv_ref: &StrVRef = strv.as_ref();
1914
1915        // Test `impl PartialEq<[&'_ str]> for StrVRef`
1916        assert_eq!(strv_ref, &["a"][..]);
1917        assert_ne!(strv_ref, &[][..]);
1918        assert_ne!(strv_ref, &["a", "b"][..]);
1919        assert_ne!(strv_ref, &["b"][..]);
1920    }
1921
1922    #[test]
1923    fn test_from_ptr_slice() {
1924        let items = [
1925            GStringPtr::from("a"),
1926            GStringPtr::from("b"),
1927            GStringPtr::from("c"),
1928        ];
1929        let ptr_slice: crate::PtrSlice<GStringPtr> = crate::PtrSlice::from(&items[..]);
1930        let strv: StrV = ptr_slice.into();
1931
1932        for (i, item) in items.into_iter().enumerate() {
1933            assert_eq!(strv[i], item);
1934        }
1935    }
1936
1937    #[test]
1938    fn test_into_ptr_slice() {
1939        let items = [crate::gstr!("a"), crate::gstr!("b"), crate::gstr!("c")];
1940        let strv: StrV = StrV::from(&items[..]);
1941        let ptr_slice: crate::PtrSlice<GStringPtr> = strv.into();
1942
1943        for (i, item) in items.into_iter().enumerate() {
1944            assert_eq!(ptr_slice[i], item);
1945        }
1946    }
1947
1948    #[test]
1949    fn test_clear_no_double_free() {
1950        let mut strv = StrV::from(&["one", "two", "three"][..]);
1951        assert_eq!(strv.len(), 3);
1952        strv.clear();
1953        assert_eq!(strv.len(), 0);
1954        // drop must not double-free
1955    }
1956}