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 Clone for StrV {
441    #[inline]
442    fn clone(&self) -> Self {
443        unsafe {
444            let mut s = Self::with_capacity(self.len());
445            for (i, item) in self.iter().enumerate() {
446                *s.ptr.as_ptr().add(i) = GString::from(item.as_str()).into_glib_ptr();
447            }
448            s.len = self.len();
449            *s.ptr.as_ptr().add(s.len) = ptr::null_mut();
450            s
451        }
452    }
453}
454
455impl StrV {
456    // rustdoc-stripper-ignore-next
457    /// Borrows a C array.
458    #[inline]
459    pub unsafe fn from_glib_borrow<'a>(ptr: *const *const c_char) -> &'a [GStringPtr] {
460        unsafe {
461            let mut len = 0;
462            if !ptr.is_null() {
463                while !(*ptr.add(len)).is_null() {
464                    len += 1;
465                }
466            }
467            Self::from_glib_borrow_num(ptr, len)
468        }
469    }
470
471    // rustdoc-stripper-ignore-next
472    /// Borrows a C array.
473    #[inline]
474    pub unsafe fn from_glib_borrow_num<'a>(
475        ptr: *const *const c_char,
476        len: usize,
477    ) -> &'a [GStringPtr] {
478        unsafe {
479            debug_assert!(!ptr.is_null() || len == 0);
480
481            if len == 0 {
482                &[]
483            } else {
484                std::slice::from_raw_parts(ptr as *const GStringPtr, len)
485            }
486        }
487    }
488
489    // rustdoc-stripper-ignore-next
490    /// Create a new `StrV` around a C array.
491    #[inline]
492    pub unsafe fn from_glib_none_num(
493        ptr: *const *const c_char,
494        len: usize,
495        _null_terminated: bool,
496    ) -> Self {
497        unsafe {
498            debug_assert!(!ptr.is_null() || len == 0);
499
500            if len == 0 {
501                StrV::default()
502            } else {
503                // Allocate space for len + 1 pointers, one pointer for each string and a trailing
504                // null pointer.
505                let new_ptr =
506                    ffi::g_malloc(mem::size_of::<*mut c_char>() * (len + 1)) as *mut *mut c_char;
507
508                // Need to clone every item because we don't own it here
509                for i in 0..len {
510                    let p = ptr.add(i) as *mut *const c_char;
511                    let q = new_ptr.add(i) as *mut *const c_char;
512                    *q = ffi::g_strdup(*p);
513                }
514
515                *new_ptr.add(len) = ptr::null_mut();
516
517                StrV {
518                    ptr: ptr::NonNull::new_unchecked(new_ptr),
519                    len,
520                    capacity: len + 1,
521                }
522            }
523        }
524    }
525
526    // rustdoc-stripper-ignore-next
527    /// Create a new `StrV` around a C array.
528    #[inline]
529    pub unsafe fn from_glib_container_num(
530        ptr: *mut *const c_char,
531        len: usize,
532        null_terminated: bool,
533    ) -> Self {
534        unsafe {
535            debug_assert!(!ptr.is_null() || len == 0);
536
537            if len == 0 {
538                ffi::g_free(ptr as ffi::gpointer);
539                StrV::default()
540            } else {
541                // Need to clone every item because we don't own it here
542                for i in 0..len {
543                    let p = ptr.add(i);
544                    *p = ffi::g_strdup(*p);
545                }
546
547                // And now it can be handled exactly the same as `from_glib_full_num()`.
548                Self::from_glib_full_num(ptr as *mut *mut c_char, len, null_terminated)
549            }
550        }
551    }
552
553    // rustdoc-stripper-ignore-next
554    /// Create a new `StrV` around a C array.
555    #[inline]
556    pub unsafe fn from_glib_full_num(
557        ptr: *mut *mut c_char,
558        len: usize,
559        null_terminated: bool,
560    ) -> Self {
561        unsafe {
562            debug_assert!(!ptr.is_null() || len == 0);
563
564            if len == 0 {
565                ffi::g_free(ptr as ffi::gpointer);
566                StrV::default()
567            } else {
568                if null_terminated {
569                    return StrV {
570                        ptr: ptr::NonNull::new_unchecked(ptr),
571                        len,
572                        capacity: len + 1,
573                    };
574                }
575
576                // Need to re-allocate here for adding the NULL-terminator
577                let capacity = len + 1;
578                assert_ne!(capacity, 0);
579                let ptr = ffi::g_realloc(
580                    ptr as *mut _,
581                    mem::size_of::<*mut c_char>().checked_mul(capacity).unwrap(),
582                ) as *mut *mut c_char;
583                *ptr.add(len) = ptr::null_mut();
584
585                StrV {
586                    ptr: ptr::NonNull::new_unchecked(ptr),
587                    len,
588                    capacity,
589                }
590            }
591        }
592    }
593
594    // rustdoc-stripper-ignore-next
595    /// Create a new `StrV` around a `NULL`-terminated C array.
596    #[inline]
597    pub unsafe fn from_glib_none(ptr: *const *const c_char) -> Self {
598        unsafe {
599            let mut len = 0;
600            if !ptr.is_null() {
601                while !(*ptr.add(len)).is_null() {
602                    len += 1;
603                }
604            }
605
606            StrV::from_glib_none_num(ptr, len, true)
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_container(ptr: *mut *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_container_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_full(ptr: *mut *mut 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_full_num(ptr, len, true)
639        }
640    }
641
642    // rustdoc-stripper-ignore-next
643    /// Creates a new empty slice.
644    #[inline]
645    pub fn new() -> Self {
646        StrV {
647            ptr: ptr::NonNull::dangling(),
648            len: 0,
649            capacity: 0,
650        }
651    }
652
653    // rustdoc-stripper-ignore-next
654    /// Creates a new empty slice with the given capacity.
655    #[inline]
656    pub fn with_capacity(capacity: usize) -> Self {
657        let mut s = Self::new();
658        s.reserve(capacity);
659        s
660    }
661
662    // rustdoc-stripper-ignore-next
663    /// Returns the underlying pointer.
664    ///
665    /// This is guaranteed to be `NULL`-terminated.
666    #[inline]
667    pub fn as_ptr(&self) -> *const *mut c_char {
668        if self.len == 0 {
669            static EMPTY: [usize; 1] = [0];
670
671            EMPTY.as_ptr() as *const _
672        } else {
673            self.ptr.as_ptr()
674        }
675    }
676
677    // rustdoc-stripper-ignore-next
678    /// Consumes the slice and returns the underlying pointer.
679    ///
680    /// This is guaranteed to be `NULL`-terminated.
681    #[inline]
682    pub fn into_raw(mut self) -> *mut *mut c_char {
683        // Make sure to allocate a valid pointer that points to a
684        // NULL-pointer.
685        if self.len == 0 {
686            self.reserve(0);
687            unsafe {
688                *self.ptr.as_ptr().add(0) = ptr::null_mut();
689            }
690        }
691
692        self.len = 0;
693        self.capacity = 0;
694        self.ptr.as_ptr()
695    }
696
697    // rustdoc-stripper-ignore-next
698    /// Gets the length of the slice.
699    #[inline]
700    pub fn len(&self) -> usize {
701        self.len
702    }
703
704    // rustdoc-stripper-ignore-next
705    /// Returns `true` if the slice is empty.
706    #[inline]
707    pub fn is_empty(&self) -> bool {
708        self.len == 0
709    }
710
711    // rustdoc-stripper-ignore-next
712    /// Returns the capacity of the slice.
713    ///
714    /// This includes the space that is reserved for the `NULL`-terminator.
715    #[inline]
716    pub fn capacity(&self) -> usize {
717        self.capacity
718    }
719
720    // rustdoc-stripper-ignore-next
721    /// Sets the length of the slice to `len`.
722    ///
723    /// # SAFETY
724    ///
725    /// There must be at least `len` valid items and a `NULL`-terminator after the last item.
726    pub unsafe fn set_len(&mut self, len: usize) {
727        self.len = len;
728    }
729
730    // rustdoc-stripper-ignore-next
731    /// Reserves at least this much additional capacity.
732    #[allow(clippy::int_plus_one)]
733    pub fn reserve(&mut self, additional: usize) {
734        // Nothing new to reserve as there's still enough space
735        if additional < self.capacity - self.len {
736            return;
737        }
738
739        let new_capacity =
740            usize::next_power_of_two(std::cmp::max(self.len + additional, MIN_SIZE) + 1);
741        assert_ne!(new_capacity, 0);
742        assert!(new_capacity > self.capacity);
743
744        unsafe {
745            let ptr = if self.capacity == 0 {
746                ptr::null_mut()
747            } else {
748                self.ptr.as_ptr() as *mut _
749            };
750            let new_ptr = ffi::g_realloc(
751                ptr,
752                mem::size_of::<*mut c_char>()
753                    .checked_mul(new_capacity)
754                    .unwrap(),
755            ) as *mut *mut c_char;
756            if self.capacity == 0 {
757                *new_ptr = ptr::null_mut();
758            }
759            self.ptr = ptr::NonNull::new_unchecked(new_ptr);
760            self.capacity = new_capacity;
761        }
762    }
763
764    // rustdoc-stripper-ignore-next
765    /// Borrows this slice as a `&[GStringPtr]`.
766    #[inline]
767    pub const fn as_slice(&self) -> &[GStringPtr] {
768        unsafe {
769            if self.len == 0 {
770                &[]
771            } else {
772                std::slice::from_raw_parts(self.ptr.as_ptr() as *const GStringPtr, self.len)
773            }
774        }
775    }
776
777    // rustdoc-stripper-ignore-next
778    /// Removes all items from the slice.
779    #[inline]
780    pub fn clear(&mut self) {
781        unsafe {
782            for i in 0..self.len {
783                ffi::g_free(*self.ptr.as_ptr().add(i) as ffi::gpointer);
784            }
785
786            self.len = 0;
787        }
788    }
789
790    // rustdoc-stripper-ignore-next
791    /// Clones and appends all elements in `slice` to the slice.
792    #[inline]
793    pub fn extend_from_slice<S: AsRef<str>>(&mut self, other: &[S]) {
794        // Nothing new to reserve as there's still enough space
795        if other.len() >= self.capacity - self.len {
796            self.reserve(other.len());
797        }
798
799        unsafe {
800            for item in other {
801                *self.ptr.as_ptr().add(self.len) = GString::from(item.as_ref()).into_glib_ptr();
802                self.len += 1;
803
804                // Add null terminator on every iteration because `as_ref`
805                // may panic
806                *self.ptr.as_ptr().add(self.len) = ptr::null_mut();
807            }
808        }
809    }
810
811    // rustdoc-stripper-ignore-next
812    /// Inserts `item` at position `index` of the slice, shifting all elements after it to the
813    /// right.
814    #[inline]
815    pub fn insert(&mut self, index: usize, item: GString) {
816        assert!(index <= self.len);
817
818        // Nothing new to reserve as there's still enough space
819        if 1 >= self.capacity - self.len {
820            self.reserve(1);
821        }
822
823        unsafe {
824            if index == self.len {
825                *self.ptr.as_ptr().add(self.len) = item.into_glib_ptr();
826            } else {
827                let p = self.ptr.as_ptr().add(index);
828                ptr::copy(p, p.add(1), self.len - index);
829                *self.ptr.as_ptr().add(index) = item.into_glib_ptr();
830            }
831
832            self.len += 1;
833
834            *self.ptr.as_ptr().add(self.len) = ptr::null_mut();
835        }
836    }
837
838    // rustdoc-stripper-ignore-next
839    /// Pushes `item` to the end of the slice.
840    #[inline]
841    pub fn push(&mut self, item: GString) {
842        // Nothing new to reserve as there's still enough space
843        if 1 >= self.capacity - self.len {
844            self.reserve(1);
845        }
846
847        unsafe {
848            *self.ptr.as_ptr().add(self.len) = item.into_glib_ptr();
849            self.len += 1;
850
851            *self.ptr.as_ptr().add(self.len) = ptr::null_mut();
852        }
853    }
854
855    // rustdoc-stripper-ignore-next
856    /// Removes item from position `index` of the slice, shifting all elements after it to the
857    /// left.
858    #[inline]
859    pub fn remove(&mut self, index: usize) -> GString {
860        assert!(index < self.len);
861
862        unsafe {
863            let p = self.ptr.as_ptr().add(index);
864            let item = *p;
865            ptr::copy(p.add(1), p, self.len - index - 1);
866
867            self.len -= 1;
868
869            *self.ptr.as_ptr().add(self.len) = ptr::null_mut();
870
871            GString::from_glib_full(item)
872        }
873    }
874
875    // rustdoc-stripper-ignore-next
876    /// Swaps item from position `index` of the slice and returns it.
877    #[inline]
878    pub fn swap(&mut self, index: usize, new_item: GString) -> GString {
879        assert!(index < self.len);
880
881        unsafe {
882            let p = self.ptr.as_ptr().add(index);
883            let item = *p;
884            *p = new_item.into_glib_ptr();
885
886            GString::from_glib_full(item)
887        }
888    }
889
890    // rustdoc-stripper-ignore-next
891    /// Removes the last item of the slice and returns it.
892    #[inline]
893    pub fn pop(&mut self) -> Option<GString> {
894        if self.len == 0 {
895            return None;
896        }
897
898        unsafe {
899            self.len -= 1;
900            let p = self.ptr.as_ptr().add(self.len);
901            let item = *p;
902
903            *self.ptr.as_ptr().add(self.len) = ptr::null_mut();
904
905            Some(GString::from_glib_full(item))
906        }
907    }
908
909    // rustdoc-stripper-ignore-next
910    /// Shortens the slice by keeping the last `len` items.
911    ///
912    /// If there are fewer than `len` items then this has no effect.
913    #[inline]
914    pub fn truncate(&mut self, len: usize) {
915        if self.len <= len {
916            return;
917        }
918
919        unsafe {
920            while self.len > len {
921                self.len -= 1;
922                let p = self.ptr.as_ptr().add(self.len);
923                ffi::g_free(*p as ffi::gpointer);
924                *p = ptr::null_mut();
925            }
926        }
927    }
928
929    // rustdoc-stripper-ignore-next
930    /// Joins the strings into a longer string, with an optional separator
931    #[inline]
932    #[doc(alias = "g_strjoinv")]
933    pub fn join(&self, separator: Option<impl IntoGStr>) -> GString {
934        separator.run_with_gstr(|separator| unsafe {
935            from_glib_full(ffi::g_strjoinv(
936                separator.to_glib_none().0,
937                self.as_ptr() as *mut _,
938            ))
939        })
940    }
941
942    // rustdoc-stripper-ignore-next
943    /// Checks whether the `StrV` contains the specified string
944    #[inline]
945    #[doc(alias = "g_strv_contains")]
946    pub fn contains(&self, s: impl IntoGStr) -> bool {
947        s.run_with_gstr(|s| unsafe {
948            from_glib(ffi::g_strv_contains(
949                self.as_ptr() as *const _,
950                s.to_glib_none().0,
951            ))
952        })
953    }
954}
955
956impl FromGlibContainer<*mut c_char, *mut *mut c_char> for StrV {
957    #[inline]
958    unsafe fn from_glib_none_num(ptr: *mut *mut c_char, num: usize) -> Self {
959        unsafe { Self::from_glib_none_num(ptr as *const *const c_char, num, false) }
960    }
961
962    #[inline]
963    unsafe fn from_glib_container_num(ptr: *mut *mut c_char, num: usize) -> Self {
964        unsafe { Self::from_glib_container_num(ptr as *mut *const c_char, num, false) }
965    }
966
967    #[inline]
968    unsafe fn from_glib_full_num(ptr: *mut *mut c_char, num: usize) -> Self {
969        unsafe { Self::from_glib_full_num(ptr, num, false) }
970    }
971}
972
973impl FromGlibContainer<*mut c_char, *const *mut c_char> for StrV {
974    unsafe fn from_glib_none_num(ptr: *const *mut c_char, num: usize) -> Self {
975        unsafe { Self::from_glib_none_num(ptr as *const *const c_char, num, false) }
976    }
977
978    unsafe fn from_glib_container_num(_ptr: *const *mut c_char, _num: usize) -> Self {
979        unimplemented!();
980    }
981
982    unsafe fn from_glib_full_num(_ptr: *const *mut c_char, _num: usize) -> Self {
983        unimplemented!();
984    }
985}
986
987impl FromGlibPtrContainer<*mut c_char, *mut *mut c_char> for StrV {
988    #[inline]
989    unsafe fn from_glib_none(ptr: *mut *mut c_char) -> Self {
990        unsafe { Self::from_glib_none(ptr as *const *const c_char) }
991    }
992
993    #[inline]
994    unsafe fn from_glib_container(ptr: *mut *mut c_char) -> Self {
995        unsafe { Self::from_glib_container(ptr as *mut *const c_char) }
996    }
997
998    #[inline]
999    unsafe fn from_glib_full(ptr: *mut *mut c_char) -> Self {
1000        unsafe { Self::from_glib_full(ptr) }
1001    }
1002}
1003
1004impl FromGlibPtrContainer<*mut c_char, *const *mut c_char> for StrV {
1005    #[inline]
1006    unsafe fn from_glib_none(ptr: *const *mut c_char) -> Self {
1007        unsafe { Self::from_glib_none(ptr as *const *const c_char) }
1008    }
1009
1010    unsafe fn from_glib_container(_ptr: *const *mut c_char) -> Self {
1011        unimplemented!();
1012    }
1013
1014    unsafe fn from_glib_full(_ptr: *const *mut c_char) -> Self {
1015        unimplemented!();
1016    }
1017}
1018
1019impl<'a> ToGlibPtr<'a, *mut *mut c_char> for StrV {
1020    type Storage = PhantomData<&'a Self>;
1021
1022    #[inline]
1023    fn to_glib_none(&'a self) -> Stash<'a, *mut *mut c_char, Self> {
1024        Stash(self.as_ptr() as *mut _, PhantomData)
1025    }
1026
1027    #[inline]
1028    fn to_glib_container(&'a self) -> Stash<'a, *mut *mut c_char, Self> {
1029        unsafe {
1030            let ptr =
1031                ffi::g_malloc(mem::size_of::<*mut c_char>() * (self.len() + 1)) as *mut *mut c_char;
1032            ptr::copy_nonoverlapping(self.as_ptr(), ptr, self.len() + 1);
1033            Stash(ptr, PhantomData)
1034        }
1035    }
1036
1037    #[inline]
1038    fn to_glib_full(&self) -> *mut *mut c_char {
1039        self.clone().into_raw()
1040    }
1041}
1042
1043impl<'a> ToGlibPtr<'a, *const *mut c_char> for StrV {
1044    type Storage = PhantomData<&'a Self>;
1045
1046    #[inline]
1047    fn to_glib_none(&'a self) -> Stash<'a, *const *mut c_char, Self> {
1048        Stash(self.as_ptr(), PhantomData)
1049    }
1050}
1051
1052impl IntoGlibPtr<*mut *mut c_char> for StrV {
1053    #[inline]
1054    fn into_glib_ptr(self) -> *mut *mut c_char {
1055        self.into_raw()
1056    }
1057}
1058
1059impl StaticType for StrV {
1060    #[inline]
1061    fn static_type() -> crate::Type {
1062        <Vec<String>>::static_type()
1063    }
1064}
1065
1066impl StaticType for &'_ [GStringPtr] {
1067    #[inline]
1068    fn static_type() -> crate::Type {
1069        <Vec<String>>::static_type()
1070    }
1071}
1072
1073impl crate::value::ValueType for StrV {
1074    type Type = Vec<String>;
1075}
1076
1077unsafe impl<'a> crate::value::FromValue<'a> for StrV {
1078    type Checker = crate::value::GenericValueTypeChecker<Self>;
1079
1080    unsafe fn from_value(value: &'a crate::value::Value) -> Self {
1081        unsafe {
1082            let ptr = gobject_ffi::g_value_dup_boxed(value.to_glib_none().0) as *mut *mut c_char;
1083            FromGlibPtrContainer::from_glib_full(ptr)
1084        }
1085    }
1086}
1087
1088unsafe impl<'a> crate::value::FromValue<'a> for &'a [GStringPtr] {
1089    type Checker = crate::value::GenericValueTypeChecker<Self>;
1090
1091    unsafe fn from_value(value: &'a crate::value::Value) -> Self {
1092        unsafe {
1093            let ptr =
1094                gobject_ffi::g_value_get_boxed(value.to_glib_none().0) as *const *const c_char;
1095            StrV::from_glib_borrow(ptr)
1096        }
1097    }
1098}
1099
1100impl crate::value::ToValue for StrV {
1101    fn to_value(&self) -> crate::value::Value {
1102        unsafe {
1103            let mut value = crate::value::Value::for_value_type::<Self>();
1104            gobject_ffi::g_value_set_boxed(
1105                value.to_glib_none_mut().0,
1106                self.as_ptr() as ffi::gpointer,
1107            );
1108            value
1109        }
1110    }
1111
1112    fn value_type(&self) -> crate::Type {
1113        <StrV as StaticType>::static_type()
1114    }
1115}
1116
1117impl From<StrV> for crate::Value {
1118    #[inline]
1119    fn from(s: StrV) -> Self {
1120        unsafe {
1121            let mut value = crate::value::Value::for_value_type::<StrV>();
1122            gobject_ffi::g_value_take_boxed(
1123                value.to_glib_none_mut().0,
1124                s.into_raw() as ffi::gpointer,
1125            );
1126            value
1127        }
1128    }
1129}
1130
1131// rustdoc-stripper-ignore-next
1132/// A trait to accept both `&[T]` or `StrV` as an argument.
1133pub trait IntoStrV {
1134    // rustdoc-stripper-ignore-next
1135    /// Runs the given closure with a `NULL`-terminated array.
1136    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R;
1137}
1138
1139impl IntoStrV for StrV {
1140    #[inline]
1141    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1142        <&Self>::run_with_strv(&self, f)
1143    }
1144}
1145
1146impl IntoStrV for &'_ StrV {
1147    #[inline]
1148    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1149        f(unsafe { std::slice::from_raw_parts(self.as_ptr(), self.len()) })
1150    }
1151}
1152
1153// rustdoc-stripper-ignore-next
1154/// Maximum number of pointers to stack-allocate before falling back to a heap allocation.
1155///
1156/// The beginning will be used for the pointers, the remainder for the actual string content.
1157const MAX_STACK_ALLOCATION: usize = 16;
1158
1159impl IntoStrV for Vec<GString> {
1160    #[inline]
1161    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1162        self.as_slice().run_with_strv(f)
1163    }
1164}
1165
1166impl IntoStrV for Vec<&'_ GString> {
1167    #[inline]
1168    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1169        self.as_slice().run_with_strv(f)
1170    }
1171}
1172
1173impl IntoStrV for Vec<&'_ GStr> {
1174    #[inline]
1175    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1176        self.as_slice().run_with_strv(f)
1177    }
1178}
1179
1180impl IntoStrV for Vec<&'_ str> {
1181    #[inline]
1182    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1183        self.as_slice().run_with_strv(f)
1184    }
1185}
1186
1187impl IntoStrV for Vec<String> {
1188    #[inline]
1189    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1190        self.as_slice().run_with_strv(f)
1191    }
1192}
1193
1194impl IntoStrV for Vec<&'_ String> {
1195    #[inline]
1196    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1197        self.as_slice().run_with_strv(f)
1198    }
1199}
1200
1201impl IntoStrV for &[GString] {
1202    #[inline]
1203    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1204        let required_len = (self.len() + 1) * mem::size_of::<*mut c_char>();
1205
1206        if required_len < MAX_STACK_ALLOCATION * mem::size_of::<*mut c_char>() {
1207            unsafe {
1208                let mut s = mem::MaybeUninit::<[*mut c_char; MAX_STACK_ALLOCATION]>::uninit();
1209                let ptrs = s.as_mut_ptr() as *mut *mut c_char;
1210
1211                for (i, item) in self.iter().enumerate() {
1212                    *ptrs.add(i) = item.as_ptr() as *mut _;
1213                }
1214                *ptrs.add(self.len()) = ptr::null_mut();
1215
1216                f(std::slice::from_raw_parts(ptrs, self.len()))
1217            }
1218        } else {
1219            let mut s = StrV::with_capacity(self.len());
1220            s.extend_from_slice(self);
1221            s.run_with_strv(f)
1222        }
1223    }
1224}
1225
1226impl IntoStrV for &[&GString] {
1227    #[inline]
1228    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1229        let required_len = (self.len() + 1) * mem::size_of::<*mut c_char>();
1230
1231        if required_len < MAX_STACK_ALLOCATION * mem::size_of::<*mut c_char>() {
1232            unsafe {
1233                let mut s = mem::MaybeUninit::<[*mut c_char; MAX_STACK_ALLOCATION]>::uninit();
1234                let ptrs = s.as_mut_ptr() as *mut *mut c_char;
1235
1236                for (i, item) in self.iter().enumerate() {
1237                    *ptrs.add(i) = item.as_ptr() as *mut _;
1238                }
1239                *ptrs.add(self.len()) = ptr::null_mut();
1240
1241                f(std::slice::from_raw_parts(ptrs, self.len()))
1242            }
1243        } else {
1244            let mut s = StrV::with_capacity(self.len());
1245            s.extend_from_slice(self);
1246            s.run_with_strv(f)
1247        }
1248    }
1249}
1250
1251impl IntoStrV for &[&GStr] {
1252    #[inline]
1253    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1254        let required_len = (self.len() + 1) * mem::size_of::<*mut c_char>();
1255
1256        if required_len < MAX_STACK_ALLOCATION * mem::size_of::<*mut c_char>() {
1257            unsafe {
1258                let mut s = mem::MaybeUninit::<[*mut c_char; MAX_STACK_ALLOCATION]>::uninit();
1259                let ptrs = s.as_mut_ptr() as *mut *mut c_char;
1260
1261                for (i, item) in self.iter().enumerate() {
1262                    *ptrs.add(i) = item.as_ptr() as *mut _;
1263                }
1264                *ptrs.add(self.len()) = ptr::null_mut();
1265
1266                f(std::slice::from_raw_parts(ptrs, self.len()))
1267            }
1268        } else {
1269            let mut s = StrV::with_capacity(self.len());
1270            s.extend_from_slice(self);
1271            s.run_with_strv(f)
1272        }
1273    }
1274}
1275
1276impl IntoStrV for &[&str] {
1277    #[inline]
1278    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1279        let required_len = (self.len() + 1) * mem::size_of::<*mut c_char>()
1280            + self.iter().map(|s| s.len() + 1).sum::<usize>();
1281
1282        if required_len < MAX_STACK_ALLOCATION * mem::size_of::<*mut c_char>() {
1283            unsafe {
1284                let mut s = mem::MaybeUninit::<[*mut c_char; MAX_STACK_ALLOCATION]>::uninit();
1285                let ptrs = s.as_mut_ptr() as *mut *mut c_char;
1286                let mut strs = ptrs.add(self.len() + 1) as *mut c_char;
1287
1288                for (i, item) in self.iter().enumerate() {
1289                    ptr::copy_nonoverlapping(item.as_ptr() as *const _, strs, item.len());
1290                    *strs.add(item.len()) = 0;
1291                    *ptrs.add(i) = strs;
1292                    strs = strs.add(item.len() + 1);
1293                }
1294                *ptrs.add(self.len()) = ptr::null_mut();
1295
1296                f(std::slice::from_raw_parts(ptrs, self.len()))
1297            }
1298        } else {
1299            let mut s = StrV::with_capacity(self.len());
1300            s.extend_from_slice(self);
1301            s.run_with_strv(f)
1302        }
1303    }
1304}
1305
1306impl IntoStrV for &[String] {
1307    #[inline]
1308    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1309        let required_len = (self.len() + 1) * mem::size_of::<*mut c_char>()
1310            + self.iter().map(|s| s.len() + 1).sum::<usize>();
1311
1312        if required_len < MAX_STACK_ALLOCATION * mem::size_of::<*mut c_char>() {
1313            unsafe {
1314                let mut s = mem::MaybeUninit::<[*mut c_char; MAX_STACK_ALLOCATION]>::uninit();
1315                let ptrs = s.as_mut_ptr() as *mut *mut c_char;
1316                let mut strs = ptrs.add(self.len() + 1) as *mut c_char;
1317
1318                for (i, item) in self.iter().enumerate() {
1319                    ptr::copy_nonoverlapping(item.as_ptr() as *const _, strs, item.len());
1320                    *strs.add(item.len()) = 0;
1321                    *ptrs.add(i) = strs;
1322                    strs = strs.add(item.len() + 1);
1323                }
1324                *ptrs.add(self.len()) = ptr::null_mut();
1325
1326                f(std::slice::from_raw_parts(ptrs, self.len()))
1327            }
1328        } else {
1329            let mut s = StrV::with_capacity(self.len());
1330            s.extend_from_slice(self);
1331            s.run_with_strv(f)
1332        }
1333    }
1334}
1335
1336impl IntoStrV for &[&String] {
1337    #[inline]
1338    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1339        let required_len = (self.len() + 1) * mem::size_of::<*mut c_char>()
1340            + self.iter().map(|s| s.len() + 1).sum::<usize>();
1341
1342        if required_len < MAX_STACK_ALLOCATION * mem::size_of::<*mut c_char>() {
1343            unsafe {
1344                let mut s = mem::MaybeUninit::<[*mut c_char; MAX_STACK_ALLOCATION]>::uninit();
1345                let ptrs = s.as_mut_ptr() as *mut *mut c_char;
1346                let mut strs = ptrs.add(self.len() + 1) as *mut c_char;
1347
1348                for (i, item) in self.iter().enumerate() {
1349                    ptr::copy_nonoverlapping(item.as_ptr() as *const _, strs, item.len());
1350                    *strs.add(item.len()) = 0;
1351                    *ptrs.add(i) = strs;
1352                    strs = strs.add(item.len() + 1);
1353                }
1354                *ptrs.add(self.len()) = ptr::null_mut();
1355
1356                f(std::slice::from_raw_parts(ptrs, self.len()))
1357            }
1358        } else {
1359            let mut s = StrV::with_capacity(self.len());
1360            s.extend_from_slice(self);
1361            s.run_with_strv(f)
1362        }
1363    }
1364}
1365
1366impl<const N: usize> IntoStrV for [GString; N] {
1367    #[inline]
1368    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1369        self.as_slice().run_with_strv(f)
1370    }
1371}
1372
1373impl<const N: usize> IntoStrV for [&'_ GString; N] {
1374    #[inline]
1375    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1376        self.as_slice().run_with_strv(f)
1377    }
1378}
1379
1380impl<const N: usize> IntoStrV for [&'_ GStr; N] {
1381    #[inline]
1382    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1383        self.as_slice().run_with_strv(f)
1384    }
1385}
1386
1387impl<const N: usize> IntoStrV for [&'_ str; N] {
1388    #[inline]
1389    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1390        self.as_slice().run_with_strv(f)
1391    }
1392}
1393
1394impl<const N: usize> IntoStrV for [String; N] {
1395    #[inline]
1396    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1397        self.as_slice().run_with_strv(f)
1398    }
1399}
1400
1401impl<const N: usize> IntoStrV for [&'_ String; N] {
1402    #[inline]
1403    fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R {
1404        self.as_slice().run_with_strv(f)
1405    }
1406}
1407
1408// rustdoc-stripper-ignore-next
1409/// Representation of a borrowed `NULL`-terminated C array of `NULL`-terminated UTF-8 strings.
1410///
1411/// It can be constructed safely from a `&StrV` and unsafely from a pointer to a C array.
1412/// This type is very similar to `[GStringPtr]`, but with one added constraint: the underlying C array must be `NULL`-terminated.
1413#[repr(transparent)]
1414pub struct StrVRef {
1415    inner: [GStringPtr],
1416}
1417
1418impl StrVRef {
1419    // rustdoc-stripper-ignore-next
1420    /// Borrows a C array.
1421    /// # Safety
1422    ///
1423    /// The provided pointer **must** be `NULL`-terminated. It is undefined behavior to
1424    /// pass a pointer that does not uphold this condition.
1425    #[inline]
1426    pub unsafe fn from_glib_borrow<'a>(ptr: *const *const c_char) -> &'a StrVRef {
1427        unsafe {
1428            let slice = StrV::from_glib_borrow(ptr);
1429            &*(slice as *const [GStringPtr] as *const StrVRef)
1430        }
1431    }
1432
1433    // rustdoc-stripper-ignore-next
1434    /// Borrows a C array.
1435    /// # Safety
1436    ///
1437    /// The provided pointer **must** be `NULL`-terminated. It is undefined behavior to
1438    /// pass a pointer that does not uphold this condition.
1439    #[inline]
1440    pub unsafe fn from_glib_borrow_num<'a>(ptr: *const *const c_char, len: usize) -> &'a StrVRef {
1441        unsafe {
1442            let slice = StrV::from_glib_borrow_num(ptr, len);
1443            &*(slice as *const [GStringPtr] as *const StrVRef)
1444        }
1445    }
1446
1447    // rustdoc-stripper-ignore-next
1448    /// Returns the underlying pointer.
1449    ///
1450    /// This is guaranteed to be nul-terminated.
1451    #[inline]
1452    pub const fn as_ptr(&self) -> *const *const c_char {
1453        self.inner.as_ptr() as *const *const _
1454    }
1455}
1456
1457impl fmt::Debug for StrVRef {
1458    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1459        self.inner.fmt(f)
1460    }
1461}
1462
1463unsafe impl Send for StrVRef {}
1464
1465unsafe impl Sync for StrVRef {}
1466
1467impl PartialEq for StrVRef {
1468    #[inline]
1469    fn eq(&self, other: &Self) -> bool {
1470        self.inner == other.inner
1471    }
1472}
1473
1474impl Eq for StrVRef {}
1475
1476impl PartialOrd for StrVRef {
1477    #[inline]
1478    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
1479        Some(self.cmp(other))
1480    }
1481}
1482
1483impl Ord for StrVRef {
1484    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
1485        self.inner.cmp(&other.inner)
1486    }
1487}
1488
1489impl std::hash::Hash for StrVRef {
1490    #[inline]
1491    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1492        self.inner.hash(state)
1493    }
1494}
1495
1496impl PartialEq<[&'_ str]> for StrVRef {
1497    fn eq(&self, other: &[&'_ str]) -> bool {
1498        if self.len() != other.len() {
1499            return false;
1500        }
1501
1502        for (a, b) in Iterator::zip(self.iter(), other.iter()) {
1503            if a != b {
1504                return false;
1505            }
1506        }
1507
1508        true
1509    }
1510}
1511
1512impl PartialEq<StrVRef> for [&'_ str] {
1513    #[inline]
1514    fn eq(&self, other: &StrVRef) -> bool {
1515        other.eq(self)
1516    }
1517}
1518
1519impl Default for &StrVRef {
1520    #[inline]
1521    fn default() -> Self {
1522        const SLICE: &[*const c_char] = &[ptr::null()];
1523        // SAFETY: `SLICE` is indeed a valid nul-terminated array.
1524        unsafe { StrVRef::from_glib_borrow(SLICE.as_ptr()) }
1525    }
1526}
1527
1528impl std::ops::Deref for StrVRef {
1529    type Target = [GStringPtr];
1530
1531    #[inline]
1532    fn deref(&self) -> &[GStringPtr] {
1533        &self.inner
1534    }
1535}
1536
1537impl<'a> std::iter::IntoIterator for &'a StrVRef {
1538    type Item = &'a GStringPtr;
1539    type IntoIter = std::slice::Iter<'a, GStringPtr>;
1540
1541    #[inline]
1542    fn into_iter(self) -> Self::IntoIter {
1543        self.inner.iter()
1544    }
1545}
1546
1547impl<'a> From<&'a StrV> for &'a StrVRef {
1548    fn from(value: &'a StrV) -> Self {
1549        let slice = value.as_slice();
1550        // Safety: `&StrV` is a null-terminated C array of nul-terminated UTF-8 strings,
1551        // therefore `&StrV::as_slice()` return a a null-terminated slice of nul-terminated UTF-8 strings,
1552        // thus it is safe to convert it to `&CStr`.
1553        unsafe { &*(slice as *const [GStringPtr] as *const StrVRef) }
1554    }
1555}
1556
1557impl FromGlibContainer<*mut c_char, *const *const c_char> for &StrVRef {
1558    unsafe fn from_glib_none_num(ptr: *const *const c_char, num: usize) -> Self {
1559        unsafe { StrVRef::from_glib_borrow_num(ptr, num) }
1560    }
1561
1562    unsafe fn from_glib_container_num(_ptr: *const *const c_char, _num: usize) -> Self {
1563        unimplemented!();
1564    }
1565
1566    unsafe fn from_glib_full_num(_ptr: *const *const c_char, _num: usize) -> Self {
1567        unimplemented!();
1568    }
1569}
1570
1571impl FromGlibPtrContainer<*mut c_char, *const *const c_char> for &StrVRef {
1572    #[inline]
1573    unsafe fn from_glib_none(ptr: *const *const c_char) -> Self {
1574        unsafe { StrVRef::from_glib_borrow(ptr) }
1575    }
1576
1577    unsafe fn from_glib_container(_ptr: *const *const c_char) -> Self {
1578        unimplemented!();
1579    }
1580
1581    unsafe fn from_glib_full(_ptr: *const *const c_char) -> Self {
1582        unimplemented!();
1583    }
1584}
1585
1586impl<'a> ToGlibPtr<'a, *const *const c_char> for StrVRef {
1587    type Storage = PhantomData<&'a Self>;
1588
1589    #[inline]
1590    fn to_glib_none(&'a self) -> Stash<'a, *const *const c_char, Self> {
1591        Stash(self.as_ptr(), PhantomData)
1592    }
1593}
1594
1595impl IntoGlibPtr<*const *const c_char> for &StrVRef {
1596    #[inline]
1597    fn into_glib_ptr(self) -> *const *const c_char {
1598        self.as_ptr()
1599    }
1600}
1601
1602impl StaticType for StrVRef {
1603    #[inline]
1604    fn static_type() -> crate::Type {
1605        <Vec<String>>::static_type()
1606    }
1607}
1608
1609unsafe impl<'a> crate::value::FromValue<'a> for &'a StrVRef {
1610    type Checker = crate::value::GenericValueTypeChecker<Self>;
1611
1612    unsafe fn from_value(value: &'a crate::value::Value) -> Self {
1613        unsafe {
1614            let ptr =
1615                gobject_ffi::g_value_get_boxed(value.to_glib_none().0) as *const *const c_char;
1616            StrVRef::from_glib_borrow(ptr)
1617        }
1618    }
1619}
1620
1621#[cfg(test)]
1622mod test {
1623    use super::*;
1624
1625    #[test]
1626    fn test_from_glib_full() {
1627        let items = ["str1", "str2", "str3", "str4"];
1628
1629        let slice = unsafe {
1630            let ptr = ffi::g_malloc(mem::size_of::<*mut c_char>() * 4) as *mut *mut c_char;
1631            *ptr.add(0) = items[0].to_glib_full();
1632            *ptr.add(1) = items[1].to_glib_full();
1633            *ptr.add(2) = items[2].to_glib_full();
1634            *ptr.add(3) = items[3].to_glib_full();
1635
1636            StrV::from_glib_full_num(ptr, 4, false)
1637        };
1638
1639        assert_eq!(items.len(), slice.len());
1640        for (a, b) in Iterator::zip(items.iter(), slice.iter()) {
1641            assert_eq!(a, b);
1642        }
1643    }
1644
1645    #[test]
1646    fn test_from_glib_container() {
1647        let items = [
1648            crate::gstr!("str1"),
1649            crate::gstr!("str2"),
1650            crate::gstr!("str3"),
1651            crate::gstr!("str4"),
1652        ];
1653
1654        let slice = unsafe {
1655            let ptr = ffi::g_malloc(mem::size_of::<*mut c_char>() * 4) as *mut *const c_char;
1656            *ptr.add(0) = items[0].as_ptr();
1657            *ptr.add(1) = items[1].as_ptr();
1658            *ptr.add(2) = items[2].as_ptr();
1659            *ptr.add(3) = items[3].as_ptr();
1660
1661            StrV::from_glib_container_num(ptr, 4, false)
1662        };
1663
1664        assert_eq!(items.len(), slice.len());
1665        for (a, b) in Iterator::zip(items.iter(), slice.iter()) {
1666            assert_eq!(a, b);
1667        }
1668    }
1669
1670    #[test]
1671    fn test_from_glib_none() {
1672        let items = [
1673            crate::gstr!("str1"),
1674            crate::gstr!("str2"),
1675            crate::gstr!("str3"),
1676            crate::gstr!("str4"),
1677        ];
1678
1679        let slice = unsafe {
1680            let ptr = ffi::g_malloc(mem::size_of::<*mut c_char>() * 4) as *mut *const c_char;
1681            *ptr.add(0) = items[0].as_ptr();
1682            *ptr.add(1) = items[1].as_ptr();
1683            *ptr.add(2) = items[2].as_ptr();
1684            *ptr.add(3) = items[3].as_ptr();
1685
1686            let res = StrV::from_glib_none_num(ptr, 4, false);
1687            ffi::g_free(ptr as ffi::gpointer);
1688            res
1689        };
1690
1691        assert_eq!(items.len(), slice.len());
1692        for (a, b) in Iterator::zip(items.iter(), slice.iter()) {
1693            assert_eq!(a, b);
1694        }
1695    }
1696
1697    #[test]
1698    fn test_from_slice() {
1699        let items = [
1700            crate::gstr!("str1"),
1701            crate::gstr!("str2"),
1702            crate::gstr!("str3"),
1703        ];
1704
1705        let slice1 = StrV::from(&items[..]);
1706        let slice2 = StrV::from(items);
1707        assert_eq!(slice1.len(), 3);
1708        assert_eq!(slice1, slice2);
1709    }
1710
1711    #[test]
1712    fn test_safe_api() {
1713        let items = [
1714            crate::gstr!("str1"),
1715            crate::gstr!("str2"),
1716            crate::gstr!("str3"),
1717        ];
1718
1719        let mut slice = StrV::from(&items[..]);
1720        assert_eq!(slice.len(), 3);
1721        slice.push(GString::from("str4"));
1722        assert_eq!(slice.len(), 4);
1723
1724        for (a, b) in Iterator::zip(items.iter(), slice.iter()) {
1725            assert_eq!(a, b);
1726        }
1727        assert_eq!(slice[3], "str4");
1728
1729        let vec = Vec::from(slice);
1730        assert_eq!(vec.len(), 4);
1731        for (a, b) in Iterator::zip(items.iter(), vec.iter()) {
1732            assert_eq!(a, b);
1733        }
1734        assert_eq!(vec[3], "str4");
1735
1736        let mut slice = StrV::from(vec);
1737        assert_eq!(slice.len(), 4);
1738        let e = slice.pop().unwrap();
1739        assert_eq!(e, "str4");
1740        assert_eq!(slice.len(), 3);
1741        slice.insert(2, e);
1742        assert_eq!(slice.len(), 4);
1743        assert_eq!(slice[0], "str1");
1744        assert_eq!(slice[1], "str2");
1745        assert_eq!(slice[2], "str4");
1746        assert_eq!(slice[3], "str3");
1747        let e = slice.remove(2);
1748        assert_eq!(e, "str4");
1749        assert_eq!(slice.len(), 3);
1750        slice.push(e);
1751        assert_eq!(slice.len(), 4);
1752
1753        for (a, b) in Iterator::zip(items.iter(), slice.into_iter()) {
1754            assert_eq!(*a, b);
1755        }
1756    }
1757
1758    #[test]
1759    fn test_into_strv() {
1760        let items = ["str1", "str2", "str3", "str4"];
1761
1762        items[..].run_with_strv(|s| unsafe {
1763            assert!((*s.as_ptr().add(4)).is_null());
1764            assert_eq!(s.len(), items.len());
1765            let s = StrV::from_glib_borrow(s.as_ptr() as *const *const c_char);
1766            assert_eq!(s, items);
1767        });
1768
1769        Vec::from(&items[..]).run_with_strv(|s| unsafe {
1770            assert!((*s.as_ptr().add(4)).is_null());
1771            assert_eq!(s.len(), items.len());
1772            let s = StrV::from_glib_borrow(s.as_ptr() as *const *const c_char);
1773            assert_eq!(s, items);
1774        });
1775
1776        StrV::from(&items[..]).run_with_strv(|s| unsafe {
1777            assert!((*s.as_ptr().add(4)).is_null());
1778            assert_eq!(s.len(), items.len());
1779            let s = StrV::from_glib_borrow(s.as_ptr() as *const *const c_char);
1780            assert_eq!(s, items);
1781        });
1782
1783        let v = items.iter().copied().map(String::from).collect::<Vec<_>>();
1784        items.run_with_strv(|s| unsafe {
1785            assert!((*s.as_ptr().add(4)).is_null());
1786            assert_eq!(s.len(), v.len());
1787            let s = StrV::from_glib_borrow(s.as_ptr() as *const *const c_char);
1788            assert_eq!(s, items);
1789        });
1790
1791        let v = items.iter().copied().map(GString::from).collect::<Vec<_>>();
1792        items.run_with_strv(|s| unsafe {
1793            assert!((*s.as_ptr().add(4)).is_null());
1794            assert_eq!(s.len(), v.len());
1795            let s = StrV::from_glib_borrow(s.as_ptr() as *const *const c_char);
1796            assert_eq!(s, items);
1797        });
1798    }
1799
1800    #[test]
1801    fn test_join() {
1802        let items = [
1803            crate::gstr!("str1"),
1804            crate::gstr!("str2"),
1805            crate::gstr!("str3"),
1806        ];
1807
1808        let strv = StrV::from(&items[..]);
1809        assert_eq!(strv.join(None::<&str>), "str1str2str3");
1810        assert_eq!(strv.join(Some(",")), "str1,str2,str3");
1811    }
1812
1813    #[test]
1814    fn test_contains() {
1815        let items = [
1816            crate::gstr!("str1"),
1817            crate::gstr!("str2"),
1818            crate::gstr!("str3"),
1819        ];
1820
1821        let strv = StrV::from(&items[..]);
1822        assert!(strv.contains("str2"));
1823        assert!(!strv.contains("str4"));
1824    }
1825
1826    #[test]
1827    #[should_panic]
1828    fn test_reserve_overflow() {
1829        let mut strv = StrV::from(&[crate::gstr!("foo"); 3][..]);
1830
1831        // An old implementation of `reserve` used the condition `self.len +
1832        // additional + 1 <= self.capacity`, which was prone to overflow
1833        strv.reserve(usize::MAX - 3);
1834    }
1835
1836    #[test]
1837    #[should_panic]
1838    fn test_extend_from_slice_overflow() {
1839        // We need a zero-sized type because only a slice of ZST can legally
1840        // contain up to `usize::MAX` elements.
1841        #[derive(Clone, Copy)]
1842        struct ImplicitStr;
1843
1844        impl AsRef<str> for ImplicitStr {
1845            fn as_ref(&self) -> &str {
1846                ""
1847            }
1848        }
1849
1850        let mut strv = StrV::from(&[crate::gstr!(""); 3][..]);
1851
1852        // An old implementation of `extend_from_slice` used the condition
1853        // `self.len + other.len() + 1 <= self.capacity`, which was prone to
1854        // overflow
1855        strv.extend_from_slice(&[ImplicitStr; usize::MAX - 3]);
1856    }
1857
1858    #[test]
1859    fn test_extend_from_slice_panic_safe() {
1860        struct MayPanic(bool);
1861
1862        impl AsRef<str> for MayPanic {
1863            fn as_ref(&self) -> &str {
1864                if self.0 {
1865                    panic!("panicking as per request");
1866                } else {
1867                    ""
1868                }
1869            }
1870        }
1871
1872        let mut strv = StrV::from(&[crate::gstr!(""); 3][..]);
1873        strv.clear();
1874
1875        // Write one element and panic while getting the second element
1876        _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
1877            strv.extend_from_slice(&[MayPanic(false), MayPanic(true)]);
1878        }));
1879
1880        // Check that it contains up to one element is null-terminated
1881        assert!(strv.len() <= 1);
1882        unsafe {
1883            for i in 0..strv.len() {
1884                assert!(!(*strv.as_ptr().add(i)).is_null());
1885            }
1886            assert!((*strv.as_ptr().add(strv.len())).is_null());
1887        }
1888    }
1889
1890    #[test]
1891    fn test_strv_ref_eq_str_slice() {
1892        let strv = StrV::from(&[crate::gstr!("a")][..]);
1893        let strv_ref: &StrVRef = strv.as_ref();
1894
1895        // Test `impl PartialEq<[&'_ str]> for StrVRef`
1896        assert_eq!(strv_ref, &["a"][..]);
1897        assert_ne!(strv_ref, &[][..]);
1898        assert_ne!(strv_ref, &["a", "b"][..]);
1899        assert_ne!(strv_ref, &["b"][..]);
1900    }
1901}