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