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            self.len = 0;
803        }
804    }
805
806    // rustdoc-stripper-ignore-next
807    /// Clones and appends all elements in `slice` to the slice.
808    #[inline]
809    pub fn extend_from_slice<S: AsRef<str>>(&mut self, other: &[S]) {
810        // Nothing new to reserve as there's still enough space
811        if other.len() >= self.capacity - self.len {
812            self.reserve(other.len());
813        }
814
815        unsafe {
816            for item in other {
817                *self.ptr.as_ptr().add(self.len) = GString::from(item.as_ref()).into_glib_ptr();
818                self.len += 1;
819
820                // Add null terminator on every iteration because `as_ref`
821                // may panic
822                *self.ptr.as_ptr().add(self.len) = ptr::null_mut();
823            }
824        }
825    }
826
827    // rustdoc-stripper-ignore-next
828    /// Inserts `item` at position `index` of the slice, shifting all elements after it to the
829    /// right.
830    #[inline]
831    pub fn insert(&mut self, index: usize, item: GString) {
832        assert!(index <= self.len);
833
834        // Nothing new to reserve as there's still enough space
835        if 1 >= self.capacity - self.len {
836            self.reserve(1);
837        }
838
839        unsafe {
840            if index == self.len {
841                *self.ptr.as_ptr().add(self.len) = item.into_glib_ptr();
842            } else {
843                let p = self.ptr.as_ptr().add(index);
844                ptr::copy(p, p.add(1), self.len - index);
845                *self.ptr.as_ptr().add(index) = item.into_glib_ptr();
846            }
847
848            self.len += 1;
849
850            *self.ptr.as_ptr().add(self.len) = ptr::null_mut();
851        }
852    }
853
854    // rustdoc-stripper-ignore-next
855    /// Pushes `item` to the end of the slice.
856    #[inline]
857    pub fn push(&mut self, item: GString) {
858        // Nothing new to reserve as there's still enough space
859        if 1 >= self.capacity - self.len {
860            self.reserve(1);
861        }
862
863        unsafe {
864            *self.ptr.as_ptr().add(self.len) = item.into_glib_ptr();
865            self.len += 1;
866
867            *self.ptr.as_ptr().add(self.len) = ptr::null_mut();
868        }
869    }
870
871    // rustdoc-stripper-ignore-next
872    /// Removes item from position `index` of the slice, shifting all elements after it to the
873    /// left.
874    #[inline]
875    pub fn remove(&mut self, index: usize) -> GString {
876        assert!(index < self.len);
877
878        unsafe {
879            let p = self.ptr.as_ptr().add(index);
880            let item = *p;
881            ptr::copy(p.add(1), p, self.len - index - 1);
882
883            self.len -= 1;
884
885            *self.ptr.as_ptr().add(self.len) = ptr::null_mut();
886
887            GString::from_glib_full(item)
888        }
889    }
890
891    // rustdoc-stripper-ignore-next
892    /// Swaps item from position `index` of the slice and returns it.
893    #[inline]
894    pub fn swap(&mut self, index: usize, new_item: GString) -> GString {
895        assert!(index < self.len);
896
897        unsafe {
898            let p = self.ptr.as_ptr().add(index);
899            let item = *p;
900            *p = new_item.into_glib_ptr();
901
902            GString::from_glib_full(item)
903        }
904    }
905
906    // rustdoc-stripper-ignore-next
907    /// Removes the last item of the slice and returns it.
908    #[inline]
909    pub fn pop(&mut self) -> Option<GString> {
910        if self.len == 0 {
911            return None;
912        }
913
914        unsafe {
915            self.len -= 1;
916            let p = self.ptr.as_ptr().add(self.len);
917            let item = *p;
918
919            *self.ptr.as_ptr().add(self.len) = ptr::null_mut();
920
921            Some(GString::from_glib_full(item))
922        }
923    }
924
925    // rustdoc-stripper-ignore-next
926    /// Shortens the slice by keeping the last `len` items.
927    ///
928    /// If there are fewer than `len` items then this has no effect.
929    #[inline]
930    pub fn truncate(&mut self, len: usize) {
931        if self.len <= len {
932            return;
933        }
934
935        unsafe {
936            while self.len > len {
937                self.len -= 1;
938                let p = self.ptr.as_ptr().add(self.len);
939                ffi::g_free(*p as ffi::gpointer);
940                *p = ptr::null_mut();
941            }
942        }
943    }
944
945    // rustdoc-stripper-ignore-next
946    /// Joins the strings into a longer string, with an optional separator
947    #[inline]
948    #[doc(alias = "g_strjoinv")]
949    pub fn join(&self, separator: Option<impl IntoGStr>) -> GString {
950        separator.run_with_gstr(|separator| unsafe {
951            from_glib_full(ffi::g_strjoinv(
952                separator.to_glib_none().0,
953                self.as_ptr() as *mut _,
954            ))
955        })
956    }
957
958    // rustdoc-stripper-ignore-next
959    /// Checks whether the `StrV` contains the specified string
960    #[inline]
961    #[doc(alias = "g_strv_contains")]
962    pub fn contains(&self, s: impl IntoGStr) -> bool {
963        s.run_with_gstr(|s| unsafe {
964            from_glib(ffi::g_strv_contains(
965                self.as_ptr() as *const _,
966                s.to_glib_none().0,
967            ))
968        })
969    }
970}
971
972impl FromGlibContainer<*mut c_char, *mut *mut c_char> for StrV {
973    #[inline]
974    unsafe fn from_glib_none_num(ptr: *mut *mut c_char, num: usize) -> Self {
975        unsafe { Self::from_glib_none_num(ptr as *const *const c_char, num, false) }
976    }
977
978    #[inline]
979    unsafe fn from_glib_container_num(ptr: *mut *mut c_char, num: usize) -> Self {
980        unsafe { Self::from_glib_container_num(ptr as *mut *const c_char, num, false) }
981    }
982
983    #[inline]
984    unsafe fn from_glib_full_num(ptr: *mut *mut c_char, num: usize) -> Self {
985        unsafe { Self::from_glib_full_num(ptr, num, false) }
986    }
987}
988
989impl FromGlibContainer<*mut c_char, *const *mut c_char> for StrV {
990    unsafe fn from_glib_none_num(ptr: *const *mut c_char, num: usize) -> Self {
991        unsafe { Self::from_glib_none_num(ptr as *const *const c_char, num, false) }
992    }
993
994    unsafe fn from_glib_container_num(_ptr: *const *mut c_char, _num: usize) -> Self {
995        unimplemented!();
996    }
997
998    unsafe fn from_glib_full_num(_ptr: *const *mut c_char, _num: usize) -> Self {
999        unimplemented!();
1000    }
1001}
1002
1003impl FromGlibPtrContainer<*mut c_char, *mut *mut c_char> for StrV {
1004    #[inline]
1005    unsafe fn from_glib_none(ptr: *mut *mut c_char) -> Self {
1006        unsafe { Self::from_glib_none(ptr as *const *const c_char) }
1007    }
1008
1009    #[inline]
1010    unsafe fn from_glib_container(ptr: *mut *mut c_char) -> Self {
1011        unsafe { Self::from_glib_container(ptr as *mut *const c_char) }
1012    }
1013
1014    #[inline]
1015    unsafe fn from_glib_full(ptr: *mut *mut c_char) -> Self {
1016        unsafe { Self::from_glib_full(ptr) }
1017    }
1018}
1019
1020impl FromGlibPtrContainer<*mut c_char, *const *mut c_char> for StrV {
1021    #[inline]
1022    unsafe fn from_glib_none(ptr: *const *mut c_char) -> Self {
1023        unsafe { Self::from_glib_none(ptr as *const *const c_char) }
1024    }
1025
1026    unsafe fn from_glib_container(_ptr: *const *mut c_char) -> Self {
1027        unimplemented!();
1028    }
1029
1030    unsafe fn from_glib_full(_ptr: *const *mut c_char) -> Self {
1031        unimplemented!();
1032    }
1033}
1034
1035impl<'a> ToGlibPtr<'a, *mut *mut c_char> for StrV {
1036    type Storage = PhantomData<&'a Self>;
1037
1038    #[inline]
1039    fn to_glib_none(&'a self) -> Stash<'a, *mut *mut c_char, Self> {
1040        Stash(self.as_ptr() as *mut _, PhantomData)
1041    }
1042
1043    #[inline]
1044    fn to_glib_container(&'a self) -> Stash<'a, *mut *mut c_char, Self> {
1045        unsafe {
1046            let ptr =
1047                ffi::g_malloc(mem::size_of::<*mut c_char>() * (self.len() + 1)) as *mut *mut c_char;
1048            ptr::copy_nonoverlapping(self.as_ptr(), ptr, self.len() + 1);
1049            Stash(ptr, PhantomData)
1050        }
1051    }
1052
1053    #[inline]
1054    fn to_glib_full(&self) -> *mut *mut c_char {
1055        self.clone().into_raw()
1056    }
1057}
1058
1059impl<'a> ToGlibPtr<'a, *const *mut c_char> for StrV {
1060    type Storage = PhantomData<&'a Self>;
1061
1062    #[inline]
1063    fn to_glib_none(&'a self) -> Stash<'a, *const *mut c_char, Self> {
1064        Stash(self.as_ptr(), PhantomData)
1065    }
1066}
1067
1068impl IntoGlibPtr<*mut *mut c_char> for StrV {
1069    #[inline]
1070    fn into_glib_ptr(self) -> *mut *mut c_char {
1071        self.into_raw()
1072    }
1073}
1074
1075impl StaticType for StrV {
1076    #[inline]
1077    fn static_type() -> crate::Type {
1078        <Vec<String>>::static_type()
1079    }
1080}
1081
1082impl StaticType for &'_ [GStringPtr] {
1083    #[inline]
1084    fn static_type() -> crate::Type {
1085        <Vec<String>>::static_type()
1086    }
1087}
1088
1089impl crate::value::ValueType for StrV {
1090    type Type = Vec<String>;
1091}
1092
1093unsafe impl<'a> crate::value::FromValue<'a> for StrV {
1094    type Checker = crate::value::GenericValueTypeChecker<Self>;
1095
1096    unsafe fn from_value(value: &'a crate::value::Value) -> Self {
1097        unsafe {
1098            let ptr = gobject_ffi::g_value_dup_boxed(value.to_glib_none().0) as *mut *mut c_char;
1099            FromGlibPtrContainer::from_glib_full(ptr)
1100        }
1101    }
1102}
1103
1104unsafe impl<'a> crate::value::FromValue<'a> for &'a [GStringPtr] {
1105    type Checker = crate::value::GenericValueTypeChecker<Self>;
1106
1107    unsafe fn from_value(value: &'a crate::value::Value) -> Self {
1108        unsafe {
1109            let ptr =
1110                gobject_ffi::g_value_get_boxed(value.to_glib_none().0) as *const *const c_char;
1111            StrV::from_glib_borrow(ptr)
1112        }
1113    }
1114}
1115
1116impl crate::value::ToValue for StrV {
1117    fn to_value(&self) -> crate::value::Value {
1118        unsafe {
1119            let mut value = crate::value::Value::for_value_type::<Self>();
1120            gobject_ffi::g_value_set_boxed(
1121                value.to_glib_none_mut().0,
1122                self.as_ptr() as ffi::gpointer,
1123            );
1124            value
1125        }
1126    }
1127
1128    fn value_type(&self) -> crate::Type {
1129        <StrV as StaticType>::static_type()
1130    }
1131}
1132
1133impl From<StrV> for crate::Value {
1134    #[inline]
1135    fn from(s: StrV) -> Self {
1136        unsafe {
1137            let mut value = crate::value::Value::for_value_type::<StrV>();
1138            gobject_ffi::g_value_take_boxed(
1139                value.to_glib_none_mut().0,
1140                s.into_raw() as ffi::gpointer,
1141            );
1142            value
1143        }
1144    }
1145}
1146
1147// rustdoc-stripper-ignore-next
1148/// A trait to accept both `&[T]` or `StrV` as an argument.
1149pub trait IntoStrV {
1150    // rustdoc-stripper-ignore-next
1151    /// Runs the given closure with a `NULL`-terminated array.
1152    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R;
1153}
1154
1155impl IntoStrV for StrV {
1156    #[inline]
1157    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1158        <&Self>::run_with_strv(&self, f)
1159    }
1160}
1161
1162impl IntoStrV for &'_ StrV {
1163    #[inline]
1164    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1165        f(unsafe { std::slice::from_raw_parts(self.as_ptr(), self.len()) })
1166    }
1167}
1168
1169// rustdoc-stripper-ignore-next
1170/// Maximum number of pointers to stack-allocate before falling back to a heap allocation.
1171///
1172/// The beginning will be used for the pointers, the remainder for the actual string content.
1173const MAX_STACK_ALLOCATION: usize = 16;
1174
1175impl IntoStrV for Vec<GString> {
1176    #[inline]
1177    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1178        self.as_slice().run_with_strv(f)
1179    }
1180}
1181
1182impl IntoStrV for Vec<&'_ GString> {
1183    #[inline]
1184    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1185        self.as_slice().run_with_strv(f)
1186    }
1187}
1188
1189impl IntoStrV for Vec<&'_ GStr> {
1190    #[inline]
1191    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1192        self.as_slice().run_with_strv(f)
1193    }
1194}
1195
1196impl IntoStrV for Vec<&'_ str> {
1197    #[inline]
1198    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1199        self.as_slice().run_with_strv(f)
1200    }
1201}
1202
1203impl IntoStrV for Vec<String> {
1204    #[inline]
1205    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1206        self.as_slice().run_with_strv(f)
1207    }
1208}
1209
1210impl IntoStrV for Vec<&'_ String> {
1211    #[inline]
1212    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1213        self.as_slice().run_with_strv(f)
1214    }
1215}
1216
1217impl IntoStrV for &[GString] {
1218    #[inline]
1219    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1220        let required_len = (self.len() + 1) * mem::size_of::<*mut c_char>();
1221
1222        if required_len < MAX_STACK_ALLOCATION * mem::size_of::<*mut c_char>() {
1223            unsafe {
1224                let mut s = mem::MaybeUninit::<[*mut c_char; MAX_STACK_ALLOCATION]>::uninit();
1225                let ptrs = s.as_mut_ptr() as *mut *mut c_char;
1226
1227                for (i, item) in self.iter().enumerate() {
1228                    *ptrs.add(i) = item.as_ptr() as *mut _;
1229                }
1230                *ptrs.add(self.len()) = ptr::null_mut();
1231
1232                f(std::slice::from_raw_parts(ptrs, self.len()))
1233            }
1234        } else {
1235            let mut s = StrV::with_capacity(self.len());
1236            s.extend_from_slice(self);
1237            s.run_with_strv(f)
1238        }
1239    }
1240}
1241
1242impl IntoStrV for &[&GString] {
1243    #[inline]
1244    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1245        let required_len = (self.len() + 1) * mem::size_of::<*mut c_char>();
1246
1247        if required_len < MAX_STACK_ALLOCATION * mem::size_of::<*mut c_char>() {
1248            unsafe {
1249                let mut s = mem::MaybeUninit::<[*mut c_char; MAX_STACK_ALLOCATION]>::uninit();
1250                let ptrs = s.as_mut_ptr() as *mut *mut c_char;
1251
1252                for (i, item) in self.iter().enumerate() {
1253                    *ptrs.add(i) = item.as_ptr() as *mut _;
1254                }
1255                *ptrs.add(self.len()) = ptr::null_mut();
1256
1257                f(std::slice::from_raw_parts(ptrs, self.len()))
1258            }
1259        } else {
1260            let mut s = StrV::with_capacity(self.len());
1261            s.extend_from_slice(self);
1262            s.run_with_strv(f)
1263        }
1264    }
1265}
1266
1267impl IntoStrV for &[&GStr] {
1268    #[inline]
1269    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1270        let required_len = (self.len() + 1) * mem::size_of::<*mut c_char>();
1271
1272        if required_len < MAX_STACK_ALLOCATION * mem::size_of::<*mut c_char>() {
1273            unsafe {
1274                let mut s = mem::MaybeUninit::<[*mut c_char; MAX_STACK_ALLOCATION]>::uninit();
1275                let ptrs = s.as_mut_ptr() as *mut *mut c_char;
1276
1277                for (i, item) in self.iter().enumerate() {
1278                    *ptrs.add(i) = item.as_ptr() as *mut _;
1279                }
1280                *ptrs.add(self.len()) = ptr::null_mut();
1281
1282                f(std::slice::from_raw_parts(ptrs, self.len()))
1283            }
1284        } else {
1285            let mut s = StrV::with_capacity(self.len());
1286            s.extend_from_slice(self);
1287            s.run_with_strv(f)
1288        }
1289    }
1290}
1291
1292impl IntoStrV for &[&str] {
1293    #[inline]
1294    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1295        let required_len = (self.len() + 1) * mem::size_of::<*mut c_char>()
1296            + self.iter().map(|s| s.len() + 1).sum::<usize>();
1297
1298        if required_len < MAX_STACK_ALLOCATION * mem::size_of::<*mut c_char>() {
1299            unsafe {
1300                let mut s = mem::MaybeUninit::<[*mut c_char; MAX_STACK_ALLOCATION]>::uninit();
1301                let ptrs = s.as_mut_ptr() as *mut *mut c_char;
1302                let mut strs = ptrs.add(self.len() + 1) as *mut c_char;
1303
1304                for (i, item) in self.iter().enumerate() {
1305                    ptr::copy_nonoverlapping(item.as_ptr() as *const _, strs, item.len());
1306                    *strs.add(item.len()) = 0;
1307                    *ptrs.add(i) = strs;
1308                    strs = strs.add(item.len() + 1);
1309                }
1310                *ptrs.add(self.len()) = ptr::null_mut();
1311
1312                f(std::slice::from_raw_parts(ptrs, self.len()))
1313            }
1314        } else {
1315            let mut s = StrV::with_capacity(self.len());
1316            s.extend_from_slice(self);
1317            s.run_with_strv(f)
1318        }
1319    }
1320}
1321
1322impl IntoStrV for &[String] {
1323    #[inline]
1324    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1325        let required_len = (self.len() + 1) * mem::size_of::<*mut c_char>()
1326            + self.iter().map(|s| s.len() + 1).sum::<usize>();
1327
1328        if required_len < MAX_STACK_ALLOCATION * mem::size_of::<*mut c_char>() {
1329            unsafe {
1330                let mut s = mem::MaybeUninit::<[*mut c_char; MAX_STACK_ALLOCATION]>::uninit();
1331                let ptrs = s.as_mut_ptr() as *mut *mut c_char;
1332                let mut strs = ptrs.add(self.len() + 1) as *mut c_char;
1333
1334                for (i, item) in self.iter().enumerate() {
1335                    ptr::copy_nonoverlapping(item.as_ptr() as *const _, strs, item.len());
1336                    *strs.add(item.len()) = 0;
1337                    *ptrs.add(i) = strs;
1338                    strs = strs.add(item.len() + 1);
1339                }
1340                *ptrs.add(self.len()) = ptr::null_mut();
1341
1342                f(std::slice::from_raw_parts(ptrs, self.len()))
1343            }
1344        } else {
1345            let mut s = StrV::with_capacity(self.len());
1346            s.extend_from_slice(self);
1347            s.run_with_strv(f)
1348        }
1349    }
1350}
1351
1352impl IntoStrV for &[&String] {
1353    #[inline]
1354    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1355        let required_len = (self.len() + 1) * mem::size_of::<*mut c_char>()
1356            + self.iter().map(|s| s.len() + 1).sum::<usize>();
1357
1358        if required_len < MAX_STACK_ALLOCATION * mem::size_of::<*mut c_char>() {
1359            unsafe {
1360                let mut s = mem::MaybeUninit::<[*mut c_char; MAX_STACK_ALLOCATION]>::uninit();
1361                let ptrs = s.as_mut_ptr() as *mut *mut c_char;
1362                let mut strs = ptrs.add(self.len() + 1) as *mut c_char;
1363
1364                for (i, item) in self.iter().enumerate() {
1365                    ptr::copy_nonoverlapping(item.as_ptr() as *const _, strs, item.len());
1366                    *strs.add(item.len()) = 0;
1367                    *ptrs.add(i) = strs;
1368                    strs = strs.add(item.len() + 1);
1369                }
1370                *ptrs.add(self.len()) = ptr::null_mut();
1371
1372                f(std::slice::from_raw_parts(ptrs, self.len()))
1373            }
1374        } else {
1375            let mut s = StrV::with_capacity(self.len());
1376            s.extend_from_slice(self);
1377            s.run_with_strv(f)
1378        }
1379    }
1380}
1381
1382impl<const N: usize> IntoStrV for [GString; N] {
1383    #[inline]
1384    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1385        self.as_slice().run_with_strv(f)
1386    }
1387}
1388
1389impl<const N: usize> IntoStrV for [&'_ GString; N] {
1390    #[inline]
1391    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1392        self.as_slice().run_with_strv(f)
1393    }
1394}
1395
1396impl<const N: usize> IntoStrV for [&'_ GStr; N] {
1397    #[inline]
1398    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1399        self.as_slice().run_with_strv(f)
1400    }
1401}
1402
1403impl<const N: usize> IntoStrV for [&'_ str; N] {
1404    #[inline]
1405    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1406        self.as_slice().run_with_strv(f)
1407    }
1408}
1409
1410impl<const N: usize> IntoStrV for [String; N] {
1411    #[inline]
1412    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1413        self.as_slice().run_with_strv(f)
1414    }
1415}
1416
1417impl<const N: usize> IntoStrV for [&'_ String; N] {
1418    #[inline]
1419    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1420        self.as_slice().run_with_strv(f)
1421    }
1422}
1423
1424// rustdoc-stripper-ignore-next
1425/// Representation of a borrowed `NULL`-terminated C array of `NULL`-terminated UTF-8 strings.
1426///
1427/// It can be constructed safely from a `&StrV` and unsafely from a pointer to a C array.
1428/// This type is very similar to `[GStringPtr]`, but with one added constraint: the underlying C array must be `NULL`-terminated.
1429#[repr(transparent)]
1430pub struct StrVRef {
1431    inner: [GStringPtr],
1432}
1433
1434impl StrVRef {
1435    // rustdoc-stripper-ignore-next
1436    /// Borrows a C array.
1437    /// # Safety
1438    ///
1439    /// The provided pointer **must** be `NULL`-terminated. It is undefined behavior to
1440    /// pass a pointer that does not uphold this condition.
1441    #[inline]
1442    pub unsafe fn from_glib_borrow<'a>(ptr: *const *const c_char) -> &'a StrVRef {
1443        unsafe {
1444            let slice = StrV::from_glib_borrow(ptr);
1445            &*(slice as *const [GStringPtr] as *const StrVRef)
1446        }
1447    }
1448
1449    // rustdoc-stripper-ignore-next
1450    /// Borrows a C array.
1451    /// # Safety
1452    ///
1453    /// The provided pointer **must** be `NULL`-terminated. It is undefined behavior to
1454    /// pass a pointer that does not uphold this condition.
1455    #[inline]
1456    pub unsafe fn from_glib_borrow_num<'a>(ptr: *const *const c_char, len: usize) -> &'a StrVRef {
1457        unsafe {
1458            let slice = StrV::from_glib_borrow_num(ptr, len);
1459            &*(slice as *const [GStringPtr] as *const StrVRef)
1460        }
1461    }
1462
1463    // rustdoc-stripper-ignore-next
1464    /// Returns the underlying pointer.
1465    ///
1466    /// This is guaranteed to be nul-terminated.
1467    #[inline]
1468    pub const fn as_ptr(&self) -> *const *const c_char {
1469        self.inner.as_ptr() as *const *const _
1470    }
1471}
1472
1473impl fmt::Debug for StrVRef {
1474    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1475        self.inner.fmt(f)
1476    }
1477}
1478
1479unsafe impl Send for StrVRef {}
1480
1481unsafe impl Sync for StrVRef {}
1482
1483impl PartialEq for StrVRef {
1484    #[inline]
1485    fn eq(&self, other: &Self) -> bool {
1486        self.inner == other.inner
1487    }
1488}
1489
1490impl Eq for StrVRef {}
1491
1492impl PartialOrd for StrVRef {
1493    #[inline]
1494    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
1495        Some(self.cmp(other))
1496    }
1497}
1498
1499impl Ord for StrVRef {
1500    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
1501        self.inner.cmp(&other.inner)
1502    }
1503}
1504
1505impl std::hash::Hash for StrVRef {
1506    #[inline]
1507    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1508        self.inner.hash(state)
1509    }
1510}
1511
1512impl PartialEq<[&'_ str]> for StrVRef {
1513    fn eq(&self, other: &[&'_ str]) -> bool {
1514        if self.len() != other.len() {
1515            return false;
1516        }
1517
1518        for (a, b) in Iterator::zip(self.iter(), other.iter()) {
1519            if a != b {
1520                return false;
1521            }
1522        }
1523
1524        true
1525    }
1526}
1527
1528impl PartialEq<StrVRef> for [&'_ str] {
1529    #[inline]
1530    fn eq(&self, other: &StrVRef) -> bool {
1531        other.eq(self)
1532    }
1533}
1534
1535impl Default for &StrVRef {
1536    #[inline]
1537    fn default() -> Self {
1538        const SLICE: &[*const c_char] = &[ptr::null()];
1539        // SAFETY: `SLICE` is indeed a valid nul-terminated array.
1540        unsafe { StrVRef::from_glib_borrow(SLICE.as_ptr()) }
1541    }
1542}
1543
1544impl std::ops::Deref for StrVRef {
1545    type Target = [GStringPtr];
1546
1547    #[inline]
1548    fn deref(&self) -> &[GStringPtr] {
1549        &self.inner
1550    }
1551}
1552
1553impl<'a> std::iter::IntoIterator for &'a StrVRef {
1554    type Item = &'a GStringPtr;
1555    type IntoIter = std::slice::Iter<'a, GStringPtr>;
1556
1557    #[inline]
1558    fn into_iter(self) -> Self::IntoIter {
1559        self.inner.iter()
1560    }
1561}
1562
1563impl<'a> From<&'a StrV> for &'a StrVRef {
1564    fn from(value: &'a StrV) -> Self {
1565        let slice = value.as_slice();
1566        // Safety: `&StrV` is a null-terminated C array of nul-terminated UTF-8 strings,
1567        // therefore `&StrV::as_slice()` return a a null-terminated slice of nul-terminated UTF-8 strings,
1568        // thus it is safe to convert it to `&CStr`.
1569        unsafe { &*(slice as *const [GStringPtr] as *const StrVRef) }
1570    }
1571}
1572
1573impl FromGlibContainer<*mut c_char, *const *const c_char> for &StrVRef {
1574    unsafe fn from_glib_none_num(ptr: *const *const c_char, num: usize) -> Self {
1575        unsafe { StrVRef::from_glib_borrow_num(ptr, num) }
1576    }
1577
1578    unsafe fn from_glib_container_num(_ptr: *const *const c_char, _num: usize) -> Self {
1579        unimplemented!();
1580    }
1581
1582    unsafe fn from_glib_full_num(_ptr: *const *const c_char, _num: usize) -> Self {
1583        unimplemented!();
1584    }
1585}
1586
1587impl FromGlibPtrContainer<*mut c_char, *const *const c_char> for &StrVRef {
1588    #[inline]
1589    unsafe fn from_glib_none(ptr: *const *const c_char) -> Self {
1590        unsafe { StrVRef::from_glib_borrow(ptr) }
1591    }
1592
1593    unsafe fn from_glib_container(_ptr: *const *const c_char) -> Self {
1594        unimplemented!();
1595    }
1596
1597    unsafe fn from_glib_full(_ptr: *const *const c_char) -> Self {
1598        unimplemented!();
1599    }
1600}
1601
1602impl<'a> ToGlibPtr<'a, *const *const c_char> for StrVRef {
1603    type Storage = PhantomData<&'a Self>;
1604
1605    #[inline]
1606    fn to_glib_none(&'a self) -> Stash<'a, *const *const c_char, Self> {
1607        Stash(self.as_ptr(), PhantomData)
1608    }
1609}
1610
1611impl IntoGlibPtr<*const *const c_char> for &StrVRef {
1612    #[inline]
1613    fn into_glib_ptr(self) -> *const *const c_char {
1614        self.as_ptr()
1615    }
1616}
1617
1618impl StaticType for StrVRef {
1619    #[inline]
1620    fn static_type() -> crate::Type {
1621        <Vec<String>>::static_type()
1622    }
1623}
1624
1625unsafe impl<'a> crate::value::FromValue<'a> for &'a StrVRef {
1626    type Checker = crate::value::GenericValueTypeChecker<Self>;
1627
1628    unsafe fn from_value(value: &'a crate::value::Value) -> Self {
1629        unsafe {
1630            let ptr =
1631                gobject_ffi::g_value_get_boxed(value.to_glib_none().0) as *const *const c_char;
1632            StrVRef::from_glib_borrow(ptr)
1633        }
1634    }
1635}
1636
1637#[cfg(test)]
1638mod test {
1639    use super::*;
1640
1641    #[test]
1642    fn test_from_glib_full() {
1643        let items = ["str1", "str2", "str3", "str4"];
1644
1645        let slice = unsafe {
1646            let ptr = ffi::g_malloc(mem::size_of::<*mut c_char>() * 4) as *mut *mut c_char;
1647            *ptr.add(0) = items[0].to_glib_full();
1648            *ptr.add(1) = items[1].to_glib_full();
1649            *ptr.add(2) = items[2].to_glib_full();
1650            *ptr.add(3) = items[3].to_glib_full();
1651
1652            StrV::from_glib_full_num(ptr, 4, false)
1653        };
1654
1655        assert_eq!(items.len(), slice.len());
1656        for (a, b) in Iterator::zip(items.iter(), slice.iter()) {
1657            assert_eq!(a, b);
1658        }
1659    }
1660
1661    #[test]
1662    fn test_from_glib_container() {
1663        let items = [
1664            crate::gstr!("str1"),
1665            crate::gstr!("str2"),
1666            crate::gstr!("str3"),
1667            crate::gstr!("str4"),
1668        ];
1669
1670        let slice = unsafe {
1671            let ptr = ffi::g_malloc(mem::size_of::<*mut c_char>() * 4) as *mut *const c_char;
1672            *ptr.add(0) = items[0].as_ptr();
1673            *ptr.add(1) = items[1].as_ptr();
1674            *ptr.add(2) = items[2].as_ptr();
1675            *ptr.add(3) = items[3].as_ptr();
1676
1677            StrV::from_glib_container_num(ptr, 4, false)
1678        };
1679
1680        assert_eq!(items.len(), slice.len());
1681        for (a, b) in Iterator::zip(items.iter(), slice.iter()) {
1682            assert_eq!(a, b);
1683        }
1684    }
1685
1686    #[test]
1687    fn test_from_glib_none() {
1688        let items = [
1689            crate::gstr!("str1"),
1690            crate::gstr!("str2"),
1691            crate::gstr!("str3"),
1692            crate::gstr!("str4"),
1693        ];
1694
1695        let slice = unsafe {
1696            let ptr = ffi::g_malloc(mem::size_of::<*mut c_char>() * 4) as *mut *const c_char;
1697            *ptr.add(0) = items[0].as_ptr();
1698            *ptr.add(1) = items[1].as_ptr();
1699            *ptr.add(2) = items[2].as_ptr();
1700            *ptr.add(3) = items[3].as_ptr();
1701
1702            let res = StrV::from_glib_none_num(ptr, 4, false);
1703            ffi::g_free(ptr as ffi::gpointer);
1704            res
1705        };
1706
1707        assert_eq!(items.len(), slice.len());
1708        for (a, b) in Iterator::zip(items.iter(), slice.iter()) {
1709            assert_eq!(a, b);
1710        }
1711    }
1712
1713    #[test]
1714    fn test_from_slice() {
1715        let items = [
1716            crate::gstr!("str1"),
1717            crate::gstr!("str2"),
1718            crate::gstr!("str3"),
1719        ];
1720
1721        let slice1 = StrV::from(&items[..]);
1722        let slice2 = StrV::from(items);
1723        assert_eq!(slice1.len(), 3);
1724        assert_eq!(slice1, slice2);
1725    }
1726
1727    #[test]
1728    fn test_safe_api() {
1729        let items = [
1730            crate::gstr!("str1"),
1731            crate::gstr!("str2"),
1732            crate::gstr!("str3"),
1733        ];
1734
1735        let mut slice = StrV::from(&items[..]);
1736        assert_eq!(slice.len(), 3);
1737        slice.push(GString::from("str4"));
1738        assert_eq!(slice.len(), 4);
1739
1740        for (a, b) in Iterator::zip(items.iter(), slice.iter()) {
1741            assert_eq!(a, b);
1742        }
1743        assert_eq!(slice[3], "str4");
1744
1745        let vec = Vec::from(slice);
1746        assert_eq!(vec.len(), 4);
1747        for (a, b) in Iterator::zip(items.iter(), vec.iter()) {
1748            assert_eq!(a, b);
1749        }
1750        assert_eq!(vec[3], "str4");
1751
1752        let mut slice = StrV::from(vec);
1753        assert_eq!(slice.len(), 4);
1754        let e = slice.pop().unwrap();
1755        assert_eq!(e, "str4");
1756        assert_eq!(slice.len(), 3);
1757        slice.insert(2, e);
1758        assert_eq!(slice.len(), 4);
1759        assert_eq!(slice[0], "str1");
1760        assert_eq!(slice[1], "str2");
1761        assert_eq!(slice[2], "str4");
1762        assert_eq!(slice[3], "str3");
1763        let e = slice.remove(2);
1764        assert_eq!(e, "str4");
1765        assert_eq!(slice.len(), 3);
1766        slice.push(e);
1767        assert_eq!(slice.len(), 4);
1768
1769        for (a, b) in Iterator::zip(items.iter(), slice.into_iter()) {
1770            assert_eq!(*a, b);
1771        }
1772    }
1773
1774    #[test]
1775    fn test_into_strv() {
1776        let items = ["str1", "str2", "str3", "str4"];
1777
1778        items[..].run_with_strv(|s| unsafe {
1779            assert!((*s.as_ptr().add(4)).is_null());
1780            assert_eq!(s.len(), items.len());
1781            let s = StrV::from_glib_borrow(s.as_ptr() as *const *const c_char);
1782            assert_eq!(s, items);
1783        });
1784
1785        Vec::from(&items[..]).run_with_strv(|s| unsafe {
1786            assert!((*s.as_ptr().add(4)).is_null());
1787            assert_eq!(s.len(), items.len());
1788            let s = StrV::from_glib_borrow(s.as_ptr() as *const *const c_char);
1789            assert_eq!(s, items);
1790        });
1791
1792        StrV::from(&items[..]).run_with_strv(|s| unsafe {
1793            assert!((*s.as_ptr().add(4)).is_null());
1794            assert_eq!(s.len(), items.len());
1795            let s = StrV::from_glib_borrow(s.as_ptr() as *const *const c_char);
1796            assert_eq!(s, items);
1797        });
1798
1799        let v = items.iter().copied().map(String::from).collect::<Vec<_>>();
1800        items.run_with_strv(|s| unsafe {
1801            assert!((*s.as_ptr().add(4)).is_null());
1802            assert_eq!(s.len(), v.len());
1803            let s = StrV::from_glib_borrow(s.as_ptr() as *const *const c_char);
1804            assert_eq!(s, items);
1805        });
1806
1807        let v = items.iter().copied().map(GString::from).collect::<Vec<_>>();
1808        items.run_with_strv(|s| unsafe {
1809            assert!((*s.as_ptr().add(4)).is_null());
1810            assert_eq!(s.len(), v.len());
1811            let s = StrV::from_glib_borrow(s.as_ptr() as *const *const c_char);
1812            assert_eq!(s, items);
1813        });
1814    }
1815
1816    #[test]
1817    fn test_join() {
1818        let items = [
1819            crate::gstr!("str1"),
1820            crate::gstr!("str2"),
1821            crate::gstr!("str3"),
1822        ];
1823
1824        let strv = StrV::from(&items[..]);
1825        assert_eq!(strv.join(None::<&str>), "str1str2str3");
1826        assert_eq!(strv.join(Some(",")), "str1,str2,str3");
1827    }
1828
1829    #[test]
1830    fn test_contains() {
1831        let items = [
1832            crate::gstr!("str1"),
1833            crate::gstr!("str2"),
1834            crate::gstr!("str3"),
1835        ];
1836
1837        let strv = StrV::from(&items[..]);
1838        assert!(strv.contains("str2"));
1839        assert!(!strv.contains("str4"));
1840    }
1841
1842    #[test]
1843    #[should_panic]
1844    fn test_reserve_overflow() {
1845        let mut strv = StrV::from(&[crate::gstr!("foo"); 3][..]);
1846
1847        // An old implementation of `reserve` used the condition `self.len +
1848        // additional + 1 <= self.capacity`, which was prone to overflow
1849        strv.reserve(usize::MAX - 3);
1850    }
1851
1852    #[test]
1853    #[should_panic]
1854    fn test_extend_from_slice_overflow() {
1855        // We need a zero-sized type because only a slice of ZST can legally
1856        // contain up to `usize::MAX` elements.
1857        #[derive(Clone, Copy)]
1858        struct ImplicitStr;
1859
1860        impl AsRef<str> for ImplicitStr {
1861            fn as_ref(&self) -> &str {
1862                ""
1863            }
1864        }
1865
1866        let mut strv = StrV::from(&[crate::gstr!(""); 3][..]);
1867
1868        // An old implementation of `extend_from_slice` used the condition
1869        // `self.len + other.len() + 1 <= self.capacity`, which was prone to
1870        // overflow
1871        strv.extend_from_slice(&[ImplicitStr; usize::MAX - 3]);
1872    }
1873
1874    #[test]
1875    fn test_extend_from_slice_panic_safe() {
1876        struct MayPanic(bool);
1877
1878        impl AsRef<str> for MayPanic {
1879            fn as_ref(&self) -> &str {
1880                if self.0 {
1881                    panic!("panicking as per request");
1882                } else {
1883                    ""
1884                }
1885            }
1886        }
1887
1888        let mut strv = StrV::from(&[crate::gstr!(""); 3][..]);
1889        strv.clear();
1890
1891        // Write one element and panic while getting the second element
1892        _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
1893            strv.extend_from_slice(&[MayPanic(false), MayPanic(true)]);
1894        }));
1895
1896        // Check that it contains up to one element is null-terminated
1897        assert!(strv.len() <= 1);
1898        unsafe {
1899            for i in 0..strv.len() {
1900                assert!(!(*strv.as_ptr().add(i)).is_null());
1901            }
1902            assert!((*strv.as_ptr().add(strv.len())).is_null());
1903        }
1904    }
1905
1906    #[test]
1907    fn test_strv_ref_eq_str_slice() {
1908        let strv = StrV::from(&[crate::gstr!("a")][..]);
1909        let strv_ref: &StrVRef = strv.as_ref();
1910
1911        // Test `impl PartialEq<[&'_ str]> for StrVRef`
1912        assert_eq!(strv_ref, &["a"][..]);
1913        assert_ne!(strv_ref, &[][..]);
1914        assert_ne!(strv_ref, &["a", "b"][..]);
1915        assert_ne!(strv_ref, &["b"][..]);
1916    }
1917
1918    #[test]
1919    fn test_from_ptr_slice() {
1920        let items = [
1921            GStringPtr::from("a"),
1922            GStringPtr::from("b"),
1923            GStringPtr::from("c"),
1924        ];
1925        let ptr_slice: crate::PtrSlice<GStringPtr> = crate::PtrSlice::from(&items[..]);
1926        let strv: StrV = ptr_slice.into();
1927
1928        for (i, item) in items.into_iter().enumerate() {
1929            assert_eq!(strv[i], item);
1930        }
1931    }
1932
1933    #[test]
1934    fn test_into_ptr_slice() {
1935        let items = [crate::gstr!("a"), crate::gstr!("b"), crate::gstr!("c")];
1936        let strv: StrV = StrV::from(&items[..]);
1937        let ptr_slice: crate::PtrSlice<GStringPtr> = strv.into();
1938
1939        for (i, item) in items.into_iter().enumerate() {
1940            assert_eq!(ptr_slice[i], item);
1941        }
1942    }
1943}