glib/
gstring.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::{
4    borrow::{Borrow, Cow},
5    cmp::Ordering,
6    ffi::{CStr, CString, OsStr, OsString},
7    fmt, hash,
8    marker::PhantomData,
9    mem,
10    ops::Deref,
11    os::raw::{c_char, c_void},
12    path::{Path, PathBuf},
13    ptr, slice,
14};
15
16use crate::{Type, Value, ffi, gobject_ffi, prelude::*, translate::*};
17
18// rustdoc-stripper-ignore-next
19/// Representation of a borrowed [`GString`].
20///
21/// This type is very similar to [`std::ffi::CStr`], but with one added constraint: the string
22/// must also be valid UTF-8.
23#[repr(transparent)]
24pub struct GStr(str);
25
26impl GStr {
27    // rustdoc-stripper-ignore-next
28    /// Creates a GLib string wrapper from a byte slice.
29    ///
30    /// This function will cast the provided bytes to a `GStr` wrapper after ensuring that the byte
31    /// slice is valid UTF-8 and is nul-terminated.
32    #[inline]
33    pub fn from_utf8_with_nul(bytes: &[u8]) -> Result<&Self, GStrError> {
34        Self::check_trailing_nul(bytes)?;
35        std::str::from_utf8(bytes)?;
36        Ok(unsafe { mem::transmute::<&[u8], &GStr>(bytes) })
37    }
38    // rustdoc-stripper-ignore-next
39    /// Creates a GLib string wrapper from a byte slice, checking for interior nul-bytes.
40    ///
41    /// This function will cast the provided bytes to a `GStr` wrapper after ensuring that the byte
42    /// slice is valid UTF-8, is nul-terminated, and does not contain any interior nul-bytes.
43    #[inline]
44    pub fn from_utf8_with_nul_checked(bytes: &[u8]) -> Result<&Self, GStrError> {
45        Self::check_nuls(bytes)?;
46        std::str::from_utf8(bytes)?;
47        Ok(unsafe { mem::transmute::<&[u8], &GStr>(bytes) })
48    }
49    // rustdoc-stripper-ignore-next
50    /// Unsafely creates a GLib string wrapper from a byte slice.
51    ///
52    /// This function will cast the provided `bytes` to a `GStr` wrapper without performing any
53    /// sanity checks.
54    ///
55    /// # Safety
56    ///
57    /// The provided slice **must** be valid UTF-8 and nul-terminated. It is undefined behavior to
58    /// pass a slice that does not uphold those conditions.
59    #[inline]
60    pub const unsafe fn from_utf8_with_nul_unchecked(bytes: &[u8]) -> &Self {
61        unsafe {
62            debug_assert!(!bytes.is_empty() && bytes[bytes.len() - 1] == 0);
63            debug_assert!(std::str::from_utf8(bytes).is_ok());
64            mem::transmute::<&[u8], &GStr>(bytes)
65        }
66    }
67    // rustdoc-stripper-ignore-next
68    /// Creates a GLib string wrapper from a byte slice, truncating it at the first nul-byte.
69    ///
70    /// This function will cast the provided bytes to a `GStr` wrapper after ensuring that the byte
71    /// slice is valid UTF-8 and contains at least one nul-byte.
72    #[inline]
73    pub fn from_utf8_until_nul(bytes: &[u8]) -> Result<&Self, GStrError> {
74        let nul_pos = memchr::memchr(0, bytes).ok_or(GStrError::NoTrailingNul)?;
75        let bytes = unsafe { bytes.get_unchecked(..nul_pos + 1) };
76        std::str::from_utf8(bytes)?;
77        Ok(unsafe { mem::transmute::<&[u8], &GStr>(bytes) })
78    }
79    // rustdoc-stripper-ignore-next
80    /// Creates a GLib string wrapper from a string slice.
81    ///
82    /// The string slice must be terminated with a nul-byte.
83    ///
84    /// This function will cast the provided bytes to a `GStr` wrapper after ensuring
85    /// that the string slice is nul-terminated.
86    #[inline]
87    pub fn from_str_with_nul(s: &str) -> Result<&Self, GStrError> {
88        Self::check_trailing_nul(s)?;
89        Ok(unsafe { mem::transmute::<&str, &GStr>(s) })
90    }
91    // rustdoc-stripper-ignore-next
92    /// Creates a GLib string wrapper from a string slice, checking for interior nul-bytes.
93    ///
94    /// The string slice must be terminated with a nul-byte.
95    ///
96    /// This function will cast the provided bytes to a `GStr` wrapper after ensuring
97    /// that the string slice is nul-terminated and does not contain any interior nul-bytes.
98    #[inline]
99    pub fn from_str_with_nul_checked(s: &str) -> Result<&Self, GStrError> {
100        Self::check_nuls(s)?;
101        Ok(unsafe { mem::transmute::<&str, &GStr>(s) })
102    }
103    // rustdoc-stripper-ignore-next
104    /// Unsafely creates a GLib string wrapper from a string slice. The string slice must be
105    /// terminated with a nul-byte.
106    ///
107    /// This function will cast the provided string slice to a `GStr` without performing any sanity
108    /// checks.
109    ///
110    /// # Safety
111    ///
112    /// The provided string slice **must** be nul-terminated. It is undefined behavior to pass a
113    /// slice that does not uphold those conditions.
114    #[inline]
115    pub const unsafe fn from_str_with_nul_unchecked(s: &str) -> &Self {
116        unsafe {
117            debug_assert!(!s.is_empty() && s.as_bytes()[s.len() - 1] == 0);
118            mem::transmute::<&str, &GStr>(s)
119        }
120    }
121    // rustdoc-stripper-ignore-next
122    /// Creates a GLib string wrapper from a string slice, truncating it at the first nul-byte.
123    ///
124    /// The string slice must contain at least one nul-byte.
125    ///
126    /// This function will cast the provided bytes to a `GStr` wrapper after ensuring
127    /// that the string slice contains at least one nul-byte.
128    #[inline]
129    pub fn from_str_until_nul(s: &str) -> Result<&Self, GStrError> {
130        let b = s.as_bytes();
131        let nul_pos = memchr::memchr(0, b).ok_or(GStrError::NoTrailingNul)?;
132        let s = unsafe { std::str::from_utf8_unchecked(b.get_unchecked(..nul_pos + 1)) };
133        Ok(unsafe { mem::transmute::<&str, &GStr>(s) })
134    }
135    // rustdoc-stripper-ignore-next
136    /// Wraps a raw C string with a safe GLib string wrapper. The provided C string **must** be
137    /// valid UTF-8 and nul-terminated. All constraints from [`CStr::from_ptr`] also apply here.
138    ///
139    /// # Safety
140    ///
141    /// See [`CStr::from_ptr`](std::ffi::CStr#safety).
142    #[inline]
143    pub unsafe fn from_ptr<'a>(ptr: *const c_char) -> &'a Self {
144        unsafe {
145            let cstr = CStr::from_ptr(ptr);
146            Self::from_utf8_with_nul_unchecked(cstr.to_bytes_with_nul())
147        }
148    }
149    // rustdoc-stripper-ignore-next
150    /// Wraps a raw C string with a safe GLib string wrapper. The provided C string **must** be
151    /// nul-terminated. All constraints from [`std::ffi::CStr::from_ptr`] also apply here.
152    ///
153    /// If the string is valid UTF-8 then it is directly returned, otherwise `None` is returned.
154    #[inline]
155    pub unsafe fn from_ptr_checked<'a>(ptr: *const c_char) -> Option<&'a Self> {
156        unsafe {
157            let mut end_ptr = ptr::null();
158            if ffi::g_utf8_validate(ptr as *const _, -1, &mut end_ptr) != ffi::GFALSE {
159                Some(Self::from_utf8_with_nul_unchecked(slice::from_raw_parts(
160                    ptr as *const u8,
161                    end_ptr.offset_from(ptr as *const u8) as usize + 1,
162                )))
163            } else {
164                None
165            }
166        }
167    }
168    // rustdoc-stripper-ignore-next
169    /// Wraps a raw C string with a safe GLib string wrapper. The provided C string **must** be
170    /// nul-terminated. All constraints from [`std::ffi::CStr::from_ptr`] also apply here.
171    ///
172    /// If the string is valid UTF-8 then it is directly returned otherwise a copy is created with
173    /// every invalid character replaced by the Unicode replacement character (U+FFFD).
174    #[inline]
175    pub unsafe fn from_ptr_lossy<'a>(ptr: *const c_char) -> Cow<'a, Self> {
176        unsafe {
177            if let Some(gs) = Self::from_ptr_checked(ptr) {
178                Cow::Borrowed(gs)
179            } else {
180                Cow::Owned(GString::from_glib_full(ffi::g_utf8_make_valid(
181                    ptr as *const _,
182                    -1,
183                )))
184            }
185        }
186    }
187    // rustdoc-stripper-ignore-next
188    /// Converts this GLib string to a byte slice containing the trailing 0 byte.
189    ///
190    /// This function is the equivalent of [`GStr::as_bytes`] except that it will retain the
191    /// trailing nul terminator instead of chopping it off.
192    #[inline]
193    pub const fn as_bytes_with_nul(&self) -> &[u8] {
194        self.0.as_bytes()
195    }
196    // rustdoc-stripper-ignore-next
197    /// Converts this GLib string to a byte slice.
198    ///
199    /// The returned slice will **not** contain the trailing nul terminator that this GLib
200    /// string has.
201    #[inline]
202    pub const fn as_bytes(&self) -> &[u8] {
203        self.as_str().as_bytes()
204    }
205    // rustdoc-stripper-ignore-next
206    /// Returns the inner pointer to this GLib string.
207    ///
208    /// The returned pointer will be valid for as long as `self` is, and points to a contiguous
209    /// region of memory terminated with a 0 byte to represent the end of the string.
210    ///
211    /// **WARNING**
212    ///
213    /// The returned pointer is read-only; writing to it (including passing it to C code that
214    /// writes to it) causes undefined behavior. It is your responsibility to make
215    /// sure that the underlying memory is not freed too early.
216    #[inline]
217    pub const fn as_ptr(&self) -> *const c_char {
218        self.0.as_ptr() as *const _
219    }
220    // rustdoc-stripper-ignore-next
221    /// Converts this GLib string to a string slice.
222    #[inline]
223    pub const fn as_str(&self) -> &str {
224        // Clip off the nul-byte
225        unsafe {
226            std::str::from_utf8_unchecked(std::slice::from_raw_parts(
227                self.as_ptr() as *const _,
228                self.0.len() - 1,
229            ))
230        }
231    }
232    // rustdoc-stripper-ignore-next
233    /// Converts this GLib string to a C string slice, checking for interior nul-bytes.
234    ///
235    /// Returns `Err` if the string contains any interior nul-bytes.
236    #[inline]
237    pub fn to_cstr(&self) -> Result<&CStr, GStrInteriorNulError> {
238        Self::check_interior_nuls(self.as_bytes())?;
239        Ok(unsafe { self.to_cstr_unchecked() })
240    }
241    // rustdoc-stripper-ignore-next
242    /// Converts this GLib string to a C string slice, truncating it at the first nul-byte.
243    #[inline]
244    pub fn to_cstr_until_nul(&self) -> &CStr {
245        let b = self.as_bytes_with_nul();
246        let nul_pos = memchr::memchr(0, b).unwrap();
247        unsafe { CStr::from_bytes_with_nul_unchecked(b.get_unchecked(..nul_pos + 1)) }
248    }
249    // rustdoc-stripper-ignore-next
250    /// Converts this GLib string to a C string slice, without checking for interior nul-bytes.
251    ///
252    /// # Safety
253    ///
254    /// `self` **must** not contain any interior nul-bytes besides the final terminating nul-byte.
255    /// It is undefined behavior to call this on a string that contains interior nul-bytes.
256    #[inline]
257    pub const unsafe fn to_cstr_unchecked(&self) -> &CStr {
258        unsafe { CStr::from_bytes_with_nul_unchecked(self.as_bytes_with_nul()) }
259    }
260
261    #[doc(alias = "g_utf8_collate")]
262    #[doc(alias = "utf8_collate")]
263    pub fn collate(&self, other: impl IntoGStr) -> Ordering {
264        other.run_with_gstr(|other| {
265            unsafe { ffi::g_utf8_collate(self.to_glib_none().0, other.to_glib_none().0) }.cmp(&0)
266        })
267    }
268
269    #[inline]
270    fn check_nuls(s: impl AsRef<[u8]>) -> Result<(), GStrError> {
271        let s = s.as_ref();
272        if let Some(nul_pos) = memchr::memchr(0, s) {
273            if s.len() == nul_pos + 1 {
274                Ok(())
275            } else {
276                Err(GStrInteriorNulError(nul_pos).into())
277            }
278        } else {
279            Err(GStrError::NoTrailingNul)
280        }
281    }
282    #[inline]
283    fn check_trailing_nul(s: impl AsRef<[u8]>) -> Result<(), GStrError> {
284        if let Some(c) = s.as_ref().last().copied()
285            && c == 0
286        {
287            return Ok(());
288        }
289        Err(GStrError::NoTrailingNul)
290    }
291    // rustdoc-stripper-ignore-next
292    /// Returns `Err` if the string slice contains any nul-bytes.
293    #[inline]
294    pub(crate) fn check_interior_nuls(s: impl AsRef<[u8]>) -> Result<(), GStrInteriorNulError> {
295        if let Some(nul_pos) = memchr::memchr(0, s.as_ref()) {
296            Err(GStrInteriorNulError(nul_pos))
297        } else {
298            Ok(())
299        }
300    }
301    pub const NONE: Option<&'static GStr> = None;
302
303    // rustdoc-stripper-ignore-next
304    /// Interns the string and returns the canonical representation.
305    #[inline]
306    #[doc(alias = "g_intern_string")]
307    pub fn intern(&self) -> &'static GStr {
308        unsafe {
309            let s = ffi::g_intern_string(self.to_glib_none().0);
310            GStr::from_ptr(s)
311        }
312    }
313
314    // rustdoc-stripper-ignore-next
315    /// Interns the `'static` string and returns the canonical representation.
316    #[inline]
317    #[doc(alias = "g_intern_static_string")]
318    pub fn intern_static(&'static self) -> &'static GStr {
319        unsafe {
320            let s = ffi::g_intern_static_string(self.to_glib_none().0);
321            GStr::from_ptr(s)
322        }
323    }
324
325    // rustdoc-stripper-ignore-next
326    /// Interns the string and returns the canonical representation.
327    #[inline]
328    #[doc(alias = "g_intern_string")]
329    pub fn intern_from_str(s: impl AsRef<str>) -> &'static GStr {
330        unsafe {
331            let s = ffi::g_intern_string(s.as_ref().to_glib_none().0);
332            GStr::from_ptr(s)
333        }
334    }
335}
336
337// rustdoc-stripper-ignore-next
338/// Error type holding all possible failures when creating a [`GStr`] reference.
339#[derive(Debug)]
340pub enum GStrError {
341    InvalidUtf8(std::str::Utf8Error),
342    InteriorNul(GStrInteriorNulError),
343    NoTrailingNul,
344}
345
346impl std::error::Error for GStrError {
347    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
348        match self {
349            Self::InvalidUtf8(err) => std::error::Error::source(err),
350            Self::InteriorNul(err) => std::error::Error::source(err),
351            Self::NoTrailingNul => None,
352        }
353    }
354}
355
356impl fmt::Display for GStrError {
357    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
358        match self {
359            Self::InvalidUtf8(err) => fmt::Display::fmt(err, fmt),
360            Self::InteriorNul(err) => fmt::Display::fmt(err, fmt),
361            Self::NoTrailingNul => fmt.write_str("data provided is not nul terminated"),
362        }
363    }
364}
365
366impl std::convert::From<std::str::Utf8Error> for GStrError {
367    fn from(err: std::str::Utf8Error) -> Self {
368        Self::InvalidUtf8(err)
369    }
370}
371
372impl std::convert::From<GStrInteriorNulError> for GStrError {
373    fn from(err: GStrInteriorNulError) -> Self {
374        Self::InteriorNul(err)
375    }
376}
377
378// rustdoc-stripper-ignore-next
379/// Error type indicating that a buffer had unexpected nul-bytes.
380#[derive(Copy, Clone, PartialEq, Eq, Debug)]
381pub struct GStrInteriorNulError(usize);
382
383impl std::error::Error for GStrInteriorNulError {}
384
385impl fmt::Display for GStrInteriorNulError {
386    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
387        write!(
388            fmt,
389            "data provided contains an interior nul-byte at byte pos {}",
390            self.0
391        )
392    }
393}
394
395impl GStrInteriorNulError {
396    // rustdoc-stripper-ignore-next
397    /// Returns the position of the nul-byte in the slice that caused the conversion to fail.
398    #[inline]
399    pub fn nul_position(&self) -> usize {
400        self.0
401    }
402}
403
404// rustdoc-stripper-ignore-next
405/// Converts a static string literal into a static nul-terminated string.
406///
407/// The expanded expression has type [`&'static GStr`]. This macro will panic if the
408/// string literal contains any interior nul-bytes.
409///
410/// # Examples
411///
412/// ```
413/// # fn main() {
414/// use glib::{gstr, GStr, GString};
415///
416/// const MY_STRING: &GStr = gstr!("Hello");
417/// assert_eq!(MY_STRING.as_bytes_with_nul()[5], 0u8);
418/// let owned: GString = MY_STRING.to_owned();
419/// assert_eq!(MY_STRING, owned);
420/// # }
421/// ```
422///
423/// [`&'static GStr`]: crate::GStr
424#[macro_export]
425macro_rules! gstr {
426    ($s:literal) => {
427        unsafe { $crate::GStr::from_utf8_with_nul_unchecked($crate::cstr_bytes!($s)) }
428    };
429}
430
431impl fmt::Debug for GStr {
432    #[inline]
433    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
434        <&str as fmt::Debug>::fmt(&self.as_str(), f)
435    }
436}
437
438impl PartialEq for GStr {
439    #[inline]
440    fn eq(&self, other: &Self) -> bool {
441        self.as_str().eq(other.as_str())
442    }
443}
444
445impl Eq for GStr {}
446
447impl PartialOrd for GStr {
448    #[inline]
449    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
450        Some(self.cmp(other))
451    }
452}
453
454impl Ord for GStr {
455    #[inline]
456    fn cmp(&self, other: &Self) -> Ordering {
457        self.as_str().cmp(other.as_str())
458    }
459}
460
461impl hash::Hash for GStr {
462    #[inline]
463    fn hash<H: hash::Hasher>(&self, state: &mut H) {
464        self.as_str().hash(state)
465    }
466}
467
468impl Default for &GStr {
469    #[inline]
470    fn default() -> Self {
471        const SLICE: &[c_char] = &[0];
472        unsafe { GStr::from_ptr(SLICE.as_ptr()) }
473    }
474}
475
476impl fmt::Display for GStr {
477    #[inline]
478    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
479        f.write_str(self.as_str())
480    }
481}
482
483impl<'a> TryFrom<&'a CStr> for &'a GStr {
484    type Error = std::str::Utf8Error;
485    #[inline]
486    fn try_from(s: &'a CStr) -> Result<Self, Self::Error> {
487        s.to_str()?;
488        Ok(unsafe { GStr::from_utf8_with_nul_unchecked(s.to_bytes_with_nul()) })
489    }
490}
491
492impl PartialEq<GStr> for String {
493    #[inline]
494    fn eq(&self, other: &GStr) -> bool {
495        self.as_str() == other.as_str()
496    }
497}
498
499impl PartialEq<str> for GStr {
500    #[inline]
501    fn eq(&self, other: &str) -> bool {
502        self.as_str() == other
503    }
504}
505
506impl PartialEq<&str> for GStr {
507    #[inline]
508    fn eq(&self, other: &&str) -> bool {
509        self.as_str() == *other
510    }
511}
512
513impl PartialEq<GStr> for &str {
514    #[inline]
515    fn eq(&self, other: &GStr) -> bool {
516        *self == other.as_str()
517    }
518}
519
520impl PartialEq<String> for GStr {
521    #[inline]
522    fn eq(&self, other: &String) -> bool {
523        self.as_str() == other.as_str()
524    }
525}
526
527impl PartialEq<GStr> for str {
528    #[inline]
529    fn eq(&self, other: &GStr) -> bool {
530        self == other.as_str()
531    }
532}
533
534impl PartialOrd<GStr> for String {
535    #[inline]
536    fn partial_cmp(&self, other: &GStr) -> Option<Ordering> {
537        Some(self.cmp(&String::from(other.as_str())))
538    }
539}
540
541impl PartialOrd<String> for GStr {
542    #[inline]
543    fn partial_cmp(&self, other: &String) -> Option<Ordering> {
544        Some(self.as_str().cmp(other.as_str()))
545    }
546}
547
548impl PartialOrd<GStr> for str {
549    #[inline]
550    fn partial_cmp(&self, other: &GStr) -> Option<Ordering> {
551        Some(self.cmp(other.as_str()))
552    }
553}
554
555impl PartialOrd<str> for GStr {
556    #[inline]
557    fn partial_cmp(&self, other: &str) -> Option<Ordering> {
558        Some(self.as_str().cmp(other))
559    }
560}
561
562impl AsRef<GStr> for GStr {
563    #[inline]
564    fn as_ref(&self) -> &GStr {
565        self
566    }
567}
568
569impl AsRef<str> for GStr {
570    #[inline]
571    fn as_ref(&self) -> &str {
572        self.as_str()
573    }
574}
575
576impl AsRef<OsStr> for GStr {
577    #[inline]
578    fn as_ref(&self) -> &OsStr {
579        OsStr::new(self.as_str())
580    }
581}
582
583impl AsRef<Path> for GStr {
584    #[inline]
585    fn as_ref(&self) -> &Path {
586        Path::new(self.as_str())
587    }
588}
589
590impl AsRef<[u8]> for GStr {
591    #[inline]
592    fn as_ref(&self) -> &[u8] {
593        self.as_bytes()
594    }
595}
596
597impl Deref for GStr {
598    type Target = str;
599
600    #[inline]
601    fn deref(&self) -> &str {
602        self.as_str()
603    }
604}
605
606impl ToOwned for GStr {
607    type Owned = GString;
608
609    #[inline]
610    fn to_owned(&self) -> Self::Owned {
611        let b = self.as_bytes_with_nul();
612        if self.len() < INLINE_LEN {
613            let mut data = <[u8; INLINE_LEN]>::default();
614            let b = self.as_bytes();
615            unsafe { data.get_unchecked_mut(..b.len()) }.copy_from_slice(b);
616            return GString(Inner::Inline {
617                len: self.len() as u8,
618                data,
619            });
620        }
621        let inner = unsafe {
622            let copy = ffi::g_strndup(b.as_ptr() as *const c_char, b.len());
623            Inner::Foreign {
624                ptr: ptr::NonNull::new_unchecked(copy),
625                len: b.len() - 1,
626            }
627        };
628        GString(inner)
629    }
630}
631
632impl GlibPtrDefault for GStr {
633    type GlibType = *mut c_char;
634}
635
636impl StaticType for GStr {
637    #[inline]
638    fn static_type() -> Type {
639        str::static_type()
640    }
641}
642
643#[doc(hidden)]
644impl FromGlibPtrNone<*const u8> for &GStr {
645    #[inline]
646    unsafe fn from_glib_none(ptr: *const u8) -> Self {
647        unsafe {
648            debug_assert!(!ptr.is_null());
649            let cstr = CStr::from_ptr(ptr as *const _);
650            debug_assert!(cstr.to_str().is_ok(), "C string is not valid utf-8");
651            GStr::from_utf8_with_nul_unchecked(cstr.to_bytes_with_nul())
652        }
653    }
654}
655
656#[doc(hidden)]
657impl FromGlibPtrNone<*const i8> for &GStr {
658    #[inline]
659    unsafe fn from_glib_none(ptr: *const i8) -> Self {
660        unsafe { from_glib_none(ptr as *const u8) }
661    }
662}
663
664#[doc(hidden)]
665impl FromGlibPtrNone<*mut u8> for &GStr {
666    #[inline]
667    unsafe fn from_glib_none(ptr: *mut u8) -> Self {
668        unsafe { from_glib_none(ptr as *const u8) }
669    }
670}
671
672#[doc(hidden)]
673impl FromGlibPtrNone<*mut i8> for &GStr {
674    #[inline]
675    unsafe fn from_glib_none(ptr: *mut i8) -> Self {
676        unsafe { from_glib_none(ptr as *const u8) }
677    }
678}
679
680unsafe impl<'a> crate::value::FromValue<'a> for &'a GStr {
681    type Checker = crate::value::GenericValueTypeOrNoneChecker<Self>;
682
683    #[inline]
684    unsafe fn from_value(value: &'a Value) -> Self {
685        unsafe {
686            let ptr = gobject_ffi::g_value_get_string(value.to_glib_none().0);
687            let cstr = CStr::from_ptr(ptr);
688            debug_assert!(
689                cstr.to_str().is_ok(),
690                "C string in glib::Value is not valid utf-8"
691            );
692            GStr::from_utf8_with_nul_unchecked(cstr.to_bytes_with_nul())
693        }
694    }
695}
696
697impl ToValue for GStr {
698    #[inline]
699    fn to_value(&self) -> Value {
700        self.as_str().to_value()
701    }
702
703    #[inline]
704    fn value_type(&self) -> Type {
705        str::static_type()
706    }
707}
708
709impl ToValue for &GStr {
710    #[inline]
711    fn to_value(&self) -> Value {
712        (*self).to_value()
713    }
714
715    #[inline]
716    fn value_type(&self) -> Type {
717        str::static_type()
718    }
719}
720
721impl crate::value::ToValueOptional for GStr {
722    #[inline]
723    fn to_value_optional(s: Option<&Self>) -> Value {
724        crate::value::ToValueOptional::to_value_optional(s.map(|s| s.as_str()))
725    }
726}
727
728#[doc(hidden)]
729impl<'a> ToGlibPtr<'a, *const c_char> for GStr {
730    type Storage = PhantomData<&'a Self>;
731
732    #[inline]
733    fn to_glib_none(&'a self) -> Stash<'a, *const c_char, Self> {
734        Stash(self.as_ptr(), PhantomData)
735    }
736
737    #[inline]
738    fn to_glib_full(&self) -> *const c_char {
739        self.as_str().to_glib_full()
740    }
741}
742
743#[doc(hidden)]
744impl<'a> ToGlibPtr<'a, *mut c_char> for GStr {
745    type Storage = PhantomData<&'a Self>;
746
747    #[inline]
748    fn to_glib_none(&'a self) -> Stash<'a, *mut c_char, Self> {
749        Stash(self.as_ptr() as *mut c_char, PhantomData)
750    }
751
752    #[inline]
753    fn to_glib_full(&self) -> *mut c_char {
754        self.as_str().to_glib_full()
755    }
756}
757
758// rustdoc-stripper-ignore-next
759/// `NULL`-terminated UTF-8 string as stored in [`StrV`](crate::StrV).
760///
761/// Unlike [`&GStr`](crate::GStr) this does not have its length stored.
762#[repr(transparent)]
763pub struct GStringPtr(ptr::NonNull<c_char>);
764
765#[doc(hidden)]
766unsafe impl TransparentPtrType for GStringPtr {}
767
768#[doc(hidden)]
769impl GlibPtrDefault for GStringPtr {
770    type GlibType = *mut c_char;
771}
772
773impl GStringPtr {
774    // rustdoc-stripper-ignore-next
775    /// Returns the corresponding [`&GStr`](crate::GStr).
776    #[inline]
777    pub fn to_gstr(&self) -> &GStr {
778        unsafe { GStr::from_ptr(self.0.as_ptr()) }
779    }
780
781    // rustdoc-stripper-ignore-next
782    /// Returns the corresponding [`&str`].
783    #[inline]
784    pub fn as_str(&self) -> &str {
785        self.to_gstr().as_str()
786    }
787
788    // rustdoc-stripper-ignore-next
789    /// Returns the string's C pointer.
790    #[inline]
791    pub const fn as_ptr(&self) -> *const c_char {
792        self.0.as_ptr()
793    }
794
795    // rustdoc-stripper-ignore-next
796    /// Wrapper around `libc::strcmp` returning `Ordering`.
797    ///
798    /// # Safety
799    ///
800    /// `a` and `b` must be non-null pointers to nul-terminated C strings.
801    #[inline]
802    unsafe fn strcmp(a: *const c_char, b: *const c_char) -> Ordering {
803        unsafe { from_glib(libc::strcmp(a, b)) }
804    }
805}
806
807impl Clone for GStringPtr {
808    #[inline]
809    fn clone(&self) -> GStringPtr {
810        unsafe { GStringPtr(ptr::NonNull::new_unchecked(ffi::g_strdup(self.0.as_ptr()))) }
811    }
812}
813
814impl Deref for GStringPtr {
815    type Target = str;
816
817    #[inline]
818    fn deref(&self) -> &str {
819        self.as_str()
820    }
821}
822
823impl IntoGlibPtr<*mut c_char> for GStringPtr {
824    #[inline]
825    fn into_glib_ptr(self) -> *mut c_char {
826        self.0.as_ptr()
827    }
828}
829
830impl Drop for GStringPtr {
831    #[inline]
832    fn drop(&mut self) {
833        unsafe {
834            ffi::g_free(self.0.as_ptr() as *mut _);
835        }
836    }
837}
838
839impl fmt::Debug for GStringPtr {
840    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
841        <&GStr as fmt::Debug>::fmt(&self.to_gstr(), f)
842    }
843}
844
845impl fmt::Display for GStringPtr {
846    #[inline]
847    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
848        f.write_str(self.as_str())
849    }
850}
851
852impl Eq for GStringPtr {}
853
854impl PartialEq for GStringPtr {
855    #[inline]
856    fn eq(&self, other: &GStringPtr) -> bool {
857        self.partial_cmp(other) == Some(Ordering::Equal)
858    }
859}
860
861impl PartialEq<GStringPtr> for String {
862    #[inline]
863    fn eq(&self, other: &GStringPtr) -> bool {
864        self.partial_cmp(other) == Some(Ordering::Equal)
865    }
866}
867
868impl PartialEq<GStringPtr> for GString {
869    #[inline]
870    fn eq(&self, other: &GStringPtr) -> bool {
871        self.partial_cmp(other) == Some(Ordering::Equal)
872    }
873}
874
875impl PartialEq<str> for GStringPtr {
876    #[inline]
877    fn eq(&self, other: &str) -> bool {
878        self.partial_cmp(other) == Some(Ordering::Equal)
879    }
880}
881
882impl PartialEq<&str> for GStringPtr {
883    #[inline]
884    fn eq(&self, other: &&str) -> bool {
885        self.partial_cmp(other) == Some(Ordering::Equal)
886    }
887}
888
889impl PartialEq<GStr> for GStringPtr {
890    #[inline]
891    fn eq(&self, other: &GStr) -> bool {
892        self.partial_cmp(other) == Some(Ordering::Equal)
893    }
894}
895
896impl PartialEq<&GStr> for GStringPtr {
897    #[inline]
898    fn eq(&self, other: &&GStr) -> bool {
899        self.partial_cmp(other) == Some(Ordering::Equal)
900    }
901}
902
903impl PartialEq<GStringPtr> for &str {
904    #[inline]
905    fn eq(&self, other: &GStringPtr) -> bool {
906        self.partial_cmp(other) == Some(Ordering::Equal)
907    }
908}
909
910impl PartialEq<GStringPtr> for &GStr {
911    #[inline]
912    fn eq(&self, other: &GStringPtr) -> bool {
913        self.partial_cmp(other) == Some(Ordering::Equal)
914    }
915}
916
917impl PartialEq<String> for GStringPtr {
918    #[inline]
919    fn eq(&self, other: &String) -> bool {
920        self.partial_cmp(other) == Some(Ordering::Equal)
921    }
922}
923
924impl PartialEq<GString> for GStringPtr {
925    #[inline]
926    fn eq(&self, other: &GString) -> bool {
927        self.partial_cmp(other) == Some(Ordering::Equal)
928    }
929}
930
931impl PartialEq<GStringPtr> for str {
932    #[inline]
933    fn eq(&self, other: &GStringPtr) -> bool {
934        self.partial_cmp(other) == Some(Ordering::Equal)
935    }
936}
937
938impl PartialEq<GStringPtr> for GStr {
939    #[inline]
940    fn eq(&self, other: &GStringPtr) -> bool {
941        self.partial_cmp(other) == Some(Ordering::Equal)
942    }
943}
944
945impl PartialOrd<GStringPtr> for GStringPtr {
946    #[inline]
947    fn partial_cmp(&self, other: &GStringPtr) -> Option<std::cmp::Ordering> {
948        Some(self.cmp(other))
949    }
950}
951
952impl Ord for GStringPtr {
953    #[inline]
954    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
955        unsafe { GStringPtr::strcmp(self.as_ptr(), other.as_ptr()) }
956    }
957}
958
959impl PartialOrd<GStringPtr> for String {
960    #[inline]
961    fn partial_cmp(&self, other: &GStringPtr) -> Option<std::cmp::Ordering> {
962        Some(self.as_str().cmp(other))
963    }
964}
965
966impl PartialOrd<GStringPtr> for GString {
967    #[inline]
968    fn partial_cmp(&self, other: &GStringPtr) -> Option<std::cmp::Ordering> {
969        Some(unsafe { GStringPtr::strcmp(self.as_ptr(), other.as_ptr()) })
970    }
971}
972
973impl PartialOrd<String> for GStringPtr {
974    #[inline]
975    fn partial_cmp(&self, other: &String) -> Option<std::cmp::Ordering> {
976        Some(self.as_str().cmp(other))
977    }
978}
979
980impl PartialOrd<GString> for GStringPtr {
981    #[inline]
982    fn partial_cmp(&self, other: &GString) -> Option<std::cmp::Ordering> {
983        Some(unsafe { GStringPtr::strcmp(self.as_ptr(), other.as_ptr()) })
984    }
985}
986
987impl PartialOrd<GStringPtr> for str {
988    #[inline]
989    fn partial_cmp(&self, other: &GStringPtr) -> Option<std::cmp::Ordering> {
990        Some(self.cmp(other.as_str()))
991    }
992}
993
994impl PartialOrd<GStringPtr> for GStr {
995    #[inline]
996    fn partial_cmp(&self, other: &GStringPtr) -> Option<std::cmp::Ordering> {
997        Some(unsafe { GStringPtr::strcmp(self.as_ptr(), other.as_ptr()) })
998    }
999}
1000
1001impl PartialOrd<str> for GStringPtr {
1002    #[inline]
1003    fn partial_cmp(&self, other: &str) -> Option<std::cmp::Ordering> {
1004        Some(self.as_str().cmp(other))
1005    }
1006}
1007
1008impl PartialOrd<&str> for GStringPtr {
1009    #[inline]
1010    fn partial_cmp(&self, other: &&str) -> Option<std::cmp::Ordering> {
1011        Some(self.as_str().cmp(other))
1012    }
1013}
1014
1015impl PartialOrd<GStr> for GStringPtr {
1016    #[inline]
1017    fn partial_cmp(&self, other: &GStr) -> Option<std::cmp::Ordering> {
1018        Some(unsafe { GStringPtr::strcmp(self.as_ptr(), other.as_ptr()) })
1019    }
1020}
1021
1022impl PartialOrd<&GStr> for GStringPtr {
1023    #[inline]
1024    fn partial_cmp(&self, other: &&GStr) -> Option<std::cmp::Ordering> {
1025        Some(unsafe { GStringPtr::strcmp(self.as_ptr(), other.as_ptr()) })
1026    }
1027}
1028
1029impl PartialOrd<GStringPtr> for &str {
1030    #[inline]
1031    fn partial_cmp(&self, other: &GStringPtr) -> Option<std::cmp::Ordering> {
1032        Some(self.cmp(&other.as_str()))
1033    }
1034}
1035
1036impl PartialOrd<GStringPtr> for &GStr {
1037    #[inline]
1038    fn partial_cmp(&self, other: &GStringPtr) -> Option<std::cmp::Ordering> {
1039        Some(unsafe { GStringPtr::strcmp(self.as_ptr(), other.as_ptr()) })
1040    }
1041}
1042
1043impl AsRef<GStringPtr> for GStringPtr {
1044    #[inline]
1045    fn as_ref(&self) -> &GStringPtr {
1046        self
1047    }
1048}
1049
1050impl std::hash::Hash for GStringPtr {
1051    #[inline]
1052    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1053        self.as_str().hash(state);
1054    }
1055}
1056
1057impl<T: AsRef<str>> From<T> for GStringPtr {
1058    fn from(value: T) -> Self {
1059        unsafe {
1060            let value = value.as_ref();
1061            GStringPtr(ptr::NonNull::new_unchecked(ffi::g_strndup(
1062                value.as_ptr() as *const _,
1063                value.len(),
1064            )))
1065        }
1066    }
1067}
1068
1069// size_of::<Inner>() minus two bytes for length and enum discriminant
1070const INLINE_LEN: usize =
1071    mem::size_of::<Option<Box<str>>>() + mem::size_of::<usize>() - mem::size_of::<u8>() * 2;
1072
1073// rustdoc-stripper-ignore-next
1074/// A type representing an owned, C-compatible, nul-terminated UTF-8 string.
1075///
1076/// `GString` is to <code>&[GStr]</code> as [`String`] is to <code>&[str]</code>: the former in
1077/// each pair are owned strings; the latter are borrowed references.
1078///
1079/// This type is similar to [`std::ffi::CString`], but with some special behavior. When debug
1080/// assertions are enabled, <code>[From]&lt;[String]></code> will panic if there are interior
1081/// nul-bytes. In production builds, no checks will be made for interior nul-bytes, and strings
1082/// that contain interior nul-bytes will simply end at first nul-byte when converting to a C
1083/// string.
1084///
1085/// The constructors beginning with `from_utf8` and `from_string` can also be used to further
1086/// control how interior nul-bytes are handled.
1087pub struct GString(Inner);
1088
1089enum Inner {
1090    Native(Box<str>),
1091    Foreign {
1092        ptr: ptr::NonNull<c_char>,
1093        len: usize,
1094    },
1095    Inline {
1096        len: u8,
1097        data: [u8; INLINE_LEN],
1098    },
1099}
1100
1101unsafe impl Send for GString {}
1102unsafe impl Sync for GString {}
1103
1104impl GString {
1105    // rustdoc-stripper-ignore-next
1106    /// Creates a new empty [`GString`].
1107    ///
1108    /// Does not allocate.
1109    #[inline]
1110    pub fn new() -> Self {
1111        Self(Inner::Inline {
1112            len: 0,
1113            data: Default::default(),
1114        })
1115    }
1116    // rustdoc-stripper-ignore-next
1117    /// Formats an [`Arguments`](std::fmt::Arguments) into a [`GString`].
1118    ///
1119    /// This function is the same as [`std::fmt::format`], except it returns a [`GString`]. The
1120    /// [`Arguments`](std::fmt::Arguments) instance can be created with the
1121    /// [`format_args!`](std::format_args) macro.
1122    ///
1123    /// Please note that using [`gformat!`](crate::gformat) might be preferable.
1124    pub fn format(args: fmt::Arguments) -> Self {
1125        if let Some(s) = args.as_str() {
1126            return Self::from(s);
1127        }
1128
1129        let mut s = crate::GStringBuilder::default();
1130        fmt::Write::write_fmt(&mut s, args).unwrap();
1131        s.into_string()
1132    }
1133    // rustdoc-stripper-ignore-next
1134    /// Creates a GLib string by consuming a byte vector.
1135    ///
1136    /// Takes ownership of `bytes`. Returns `Err` if it contains invalid UTF-8.
1137    ///
1138    /// A trailing nul-byte will be appended by this function.
1139    #[inline]
1140    pub fn from_utf8(bytes: Vec<u8>) -> Result<Self, std::string::FromUtf8Error> {
1141        Ok(Self::from_string_unchecked(String::from_utf8(bytes)?))
1142    }
1143    // rustdoc-stripper-ignore-next
1144    /// Creates a GLib string by consuming a byte vector, checking for interior nul-bytes.
1145    ///
1146    /// Takes ownership of `bytes`, as long as it is valid UTF-8 and does not contain any interior
1147    /// nul-bytes. Otherwise, `Err` is returned.
1148    ///
1149    /// A trailing nul-byte will be appended by this function.
1150    #[inline]
1151    pub fn from_utf8_checked(bytes: Vec<u8>) -> Result<Self, GStringFromError<Vec<u8>>> {
1152        Ok(Self::from_string_checked(String::from_utf8(bytes)?)
1153            .map_err(|e| GStringInteriorNulError(e.0.into_bytes(), e.1))?)
1154    }
1155    // rustdoc-stripper-ignore-next
1156    /// Unsafely creates a GLib string by consuming a byte vector, without checking for UTF-8 or
1157    /// interior nul-bytes.
1158    ///
1159    /// A trailing nul-byte will be appended by this function.
1160    ///
1161    /// # Safety
1162    ///
1163    /// The byte vector **must** not contain invalid UTF-8 characters. It is undefined behavior to
1164    /// pass a vector that contains invalid UTF-8.
1165    #[inline]
1166    pub unsafe fn from_utf8_unchecked(mut v: Vec<u8>) -> Self {
1167        unsafe {
1168            if v.is_empty() {
1169                Self::new()
1170            } else {
1171                v.reserve_exact(1);
1172                v.push(0);
1173                Self(Inner::Native(String::from_utf8_unchecked(v).into()))
1174            }
1175        }
1176    }
1177    // rustdoc-stripper-ignore-next
1178    /// Creates a GLib string by consuming a nul-terminated byte vector, without checking for
1179    /// interior nul-bytes.
1180    ///
1181    /// Takes ownership of `bytes`. Returns `Err` if it contains invalid UTF-8 or does not have a
1182    /// trailing nul-byte.
1183    #[inline]
1184    pub fn from_utf8_with_nul(bytes: Vec<u8>) -> Result<Self, GStringFromError<Vec<u8>>> {
1185        let s = String::from_utf8(bytes)?;
1186        if s.as_bytes().last().copied() != Some(0u8) {
1187            return Err(GStringNoTrailingNulError(s.into_bytes()).into());
1188        }
1189        if s.len() == 1 {
1190            Ok(Self::new())
1191        } else {
1192            Ok(Self(Inner::Native(s.into())))
1193        }
1194    }
1195    // rustdoc-stripper-ignore-next
1196    /// Creates a GLib string by consuming a nul-terminated byte vector.
1197    ///
1198    /// Takes ownership of `bytes`. Returns `Err` if it contains invalid UTF-8, does not have a
1199    /// trailing nul-byte, or contains interior nul-bytes.
1200    #[inline]
1201    pub fn from_utf8_with_nul_checked(bytes: Vec<u8>) -> Result<Self, GStringFromError<Vec<u8>>> {
1202        let s = Self::from_utf8_with_nul(bytes)?;
1203        if let Err(e) = GStr::check_interior_nuls(&s) {
1204            return Err(GStringInteriorNulError(s.into_bytes(), e).into());
1205        }
1206        Ok(s)
1207    }
1208    // rustdoc-stripper-ignore-next
1209    /// Creates a GLib string by consuming a byte vector, without checking for UTF-8, a trailing
1210    /// nul-byte, or interior nul-bytes.
1211    ///
1212    /// # Safety
1213    ///
1214    /// The byte vector **must** not contain invalid UTF-8 characters, and **must** have a trailing
1215    /// nul-byte. It is undefined behavior to pass a vector that does not uphold those conditions.
1216    #[inline]
1217    pub unsafe fn from_utf8_with_nul_unchecked(v: Vec<u8>) -> Self {
1218        unsafe {
1219            debug_assert!(!v.is_empty() && v[v.len() - 1] == 0);
1220            let s = if cfg!(debug_assertions) {
1221                let s = String::from_utf8(v).unwrap();
1222                GStr::check_interior_nuls(&s[..s.len() - 1]).unwrap();
1223                s
1224            } else {
1225                String::from_utf8_unchecked(v)
1226            };
1227            if s.len() == 1 {
1228                Self::new()
1229            } else {
1230                Self(Inner::Native(s.into()))
1231            }
1232        }
1233    }
1234    // rustdoc-stripper-ignore-next
1235    /// Creates a GLib string by consuming a nul-terminated byte vector, truncating it at the first
1236    /// nul-byte.
1237    ///
1238    /// Takes ownership of `bytes`. Returns `Err` if it contains invalid UTF-8 or does not contain
1239    /// at least one nul-byte.
1240    #[inline]
1241    pub fn from_utf8_until_nul(mut bytes: Vec<u8>) -> Result<Self, GStringFromError<Vec<u8>>> {
1242        let nul_pos = if let Some(nul_pos) = memchr::memchr(0, &bytes) {
1243            nul_pos
1244        } else {
1245            return Err(GStringNoTrailingNulError(bytes).into());
1246        };
1247        if nul_pos == 0 {
1248            Ok(Self::new())
1249        } else {
1250            if let Err(e) = std::str::from_utf8(unsafe { bytes.get_unchecked(..nul_pos) }) {
1251                return Err(GStringUtf8Error(bytes, e).into());
1252            }
1253            bytes.truncate(nul_pos + 1);
1254            let s = unsafe { String::from_utf8_unchecked(bytes) };
1255            Ok(Self(Inner::Native(s.into())))
1256        }
1257    }
1258    // rustdoc-stripper-ignore-next
1259    /// Creates a GLib string by consuming a string, checking for interior nul-bytes.
1260    ///
1261    /// Takes ownership of `s`, as long as it does not contain any interior nul-bytes. Otherwise,
1262    /// `Err` is returned.
1263    ///
1264    /// A trailing nul-byte will be appended by this function.
1265    #[inline]
1266    pub fn from_string_checked(s: String) -> Result<Self, GStringInteriorNulError<String>> {
1267        if let Err(e) = GStr::check_interior_nuls(&s) {
1268            return Err(GStringInteriorNulError(s, e));
1269        }
1270        Ok(Self::from_string_unchecked(s))
1271    }
1272    // rustdoc-stripper-ignore-next
1273    /// Creates a GLib string by consuming a string, without checking for interior nul-bytes.
1274    ///
1275    /// A trailing nul-byte will be appended by this function.
1276    #[inline]
1277    pub fn from_string_unchecked(mut s: String) -> Self {
1278        if s.is_empty() {
1279            Self::new()
1280        } else {
1281            s.reserve_exact(1);
1282            s.push('\0');
1283            Self(Inner::Native(s.into()))
1284        }
1285    }
1286    // rustdoc-stripper-ignore-next
1287    /// Wraps a raw C string with a safe GLib string wrapper. The provided C string **must** be
1288    /// nul-terminated. All constraints from [`std::ffi::CStr::from_ptr`] also apply here.
1289    ///
1290    /// If the string is valid UTF-8 then it is directly returned otherwise a copy is created with
1291    /// every invalid character replaced by the Unicode replacement character (U+FFFD).
1292    #[inline]
1293    pub unsafe fn from_ptr_lossy<'a>(ptr: *const c_char) -> Cow<'a, GStr> {
1294        unsafe { GStr::from_ptr_lossy(ptr) }
1295    }
1296
1297    // rustdoc-stripper-ignore-next
1298    /// Wraps a raw C string with a safe GLib string wrapper. The provided C string **must** be
1299    /// nul-terminated. All constraints from [`std::ffi::CStr::from_ptr`] also apply here.
1300    ///
1301    /// `len` is the length without the nul-terminator, i.e. if `len == 0` is passed then `*ptr`
1302    /// must be the nul-terminator.
1303    #[inline]
1304    pub unsafe fn from_ptr_and_len_unchecked(ptr: *const c_char, len: usize) -> Self {
1305        unsafe {
1306            debug_assert!(!ptr.is_null());
1307
1308            GString(Inner::Foreign {
1309                ptr: ptr::NonNull::new_unchecked(ptr as *mut _),
1310                len,
1311            })
1312        }
1313    }
1314
1315    // rustdoc-stripper-ignore-next
1316    /// Return the `GString` as string slice.
1317    #[inline]
1318    pub fn as_str(&self) -> &str {
1319        unsafe {
1320            let (ptr, len) = match self.0 {
1321                Inner::Native(ref s) => (s.as_ptr(), s.len() - 1),
1322                Inner::Foreign { ptr, len } => (ptr.as_ptr() as *const u8, len),
1323                Inner::Inline { len, ref data } => (data.as_ptr(), len as usize),
1324            };
1325            if len == 0 {
1326                ""
1327            } else {
1328                let slice = slice::from_raw_parts(ptr, len);
1329                std::str::from_utf8_unchecked(slice)
1330            }
1331        }
1332    }
1333
1334    // rustdoc-stripper-ignore-next
1335    /// Extracts the [`GStr`] containing the entire string.
1336    #[inline]
1337    pub fn as_gstr(&self) -> &GStr {
1338        let bytes = match self.0 {
1339            Inner::Native(ref s) => s.as_bytes(),
1340            Inner::Foreign { len: 0, .. } => &[0],
1341            Inner::Foreign { ptr, len } => unsafe {
1342                slice::from_raw_parts(ptr.as_ptr() as *const _, len + 1)
1343            },
1344            Inner::Inline { len, ref data } => unsafe { data.get_unchecked(..len as usize + 1) },
1345        };
1346        unsafe { GStr::from_utf8_with_nul_unchecked(bytes) }
1347    }
1348
1349    // rustdoc-stripper-ignore-next
1350    /// Return the underlying pointer of the `GString`.
1351    #[inline]
1352    pub fn as_ptr(&self) -> *const c_char {
1353        match self.0 {
1354            Inner::Native(ref s) => s.as_ptr() as *const _,
1355            Inner::Foreign { ptr, .. } => ptr.as_ptr(),
1356            Inner::Inline { ref data, .. } => data.as_ptr() as *const _,
1357        }
1358    }
1359
1360    // rustdoc-stripper-ignore-next
1361    /// Consumes the `GString` and returns the underlying byte buffer.
1362    ///
1363    /// The returned buffer is not guaranteed to contain a trailing nul-byte.
1364    #[inline]
1365    pub fn into_bytes(mut self) -> Vec<u8> {
1366        match &mut self.0 {
1367            Inner::Native(s) => {
1368                let mut s = String::from(mem::replace(s, "".into()));
1369                let _nul = s.pop();
1370                debug_assert_eq!(_nul, Some('\0'));
1371                s.into_bytes()
1372            }
1373            Inner::Foreign { ptr, len } => {
1374                let bytes = unsafe { slice::from_raw_parts(ptr.as_ptr() as *const u8, *len - 1) };
1375                bytes.to_owned()
1376            }
1377            Inner::Inline { len, data } => {
1378                unsafe { data.get_unchecked(..*len as usize) }.to_owned()
1379            }
1380        }
1381    }
1382
1383    // rustdoc-stripper-ignore-next
1384    /// Consumes the `GString` and returns the underlying byte buffer, with trailing nul-byte.
1385    #[inline]
1386    pub fn into_bytes_with_nul(mut self) -> Vec<u8> {
1387        match &mut self.0 {
1388            Inner::Native(s) => str::into_boxed_bytes(mem::replace(s, "".into())).into(),
1389            Inner::Foreign { ptr, len } => {
1390                let bytes = unsafe { slice::from_raw_parts(ptr.as_ptr() as *const u8, *len) };
1391                bytes.to_owned()
1392            }
1393            Inner::Inline { len, data } => {
1394                unsafe { data.get_unchecked(..*len as usize + 1) }.to_owned()
1395            }
1396        }
1397    }
1398}
1399
1400// rustdoc-stripper-ignore-next
1401/// Creates a [`GString`] using interpolation of runtime expressions.
1402///
1403/// This macro is the same as [`std::format!`] except it returns a [`GString`]. It is faster than
1404/// creating a [`String`] and then converting it manually to a [`GString`].
1405#[macro_export]
1406macro_rules! gformat {
1407    ($($arg:tt)*) => { $crate::GString::format(std::format_args!($($arg)*)) };
1408}
1409
1410// rustdoc-stripper-ignore-next
1411/// Error type indicating that a buffer did not have a trailing nul-byte.
1412///
1413/// `T` is the type of the value the conversion was attempted from.
1414#[derive(Clone, PartialEq, Eq, Debug)]
1415pub struct GStringNoTrailingNulError<T>(T);
1416
1417impl<T> std::error::Error for GStringNoTrailingNulError<T> where T: fmt::Debug {}
1418
1419impl<T> fmt::Display for GStringNoTrailingNulError<T> {
1420    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
1421        fmt.write_str("data provided is not nul terminated")
1422    }
1423}
1424
1425impl<T> GStringNoTrailingNulError<T> {
1426    // rustdoc-stripper-ignore-next
1427    /// Returns the original value that was attempted to convert to [`GString`].
1428    #[inline]
1429    pub fn into_inner(self) -> T {
1430        self.0
1431    }
1432}
1433
1434// rustdoc-stripper-ignore-next
1435/// Error type indicating that a buffer had unexpected nul-bytes.
1436///
1437/// `T` is the type of the value the conversion was attempted from.
1438#[derive(Clone, PartialEq, Eq, Debug)]
1439pub struct GStringInteriorNulError<T>(T, GStrInteriorNulError);
1440
1441impl<T> std::error::Error for GStringInteriorNulError<T> where T: fmt::Debug {}
1442
1443impl<T> fmt::Display for GStringInteriorNulError<T> {
1444    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
1445        fmt::Display::fmt(&self.1, fmt)
1446    }
1447}
1448
1449impl<T> GStringInteriorNulError<T> {
1450    // rustdoc-stripper-ignore-next
1451    /// Returns the original value that was attempted to convert to [`GString`].
1452    #[inline]
1453    pub fn into_inner(self) -> T {
1454        self.0
1455    }
1456    // rustdoc-stripper-ignore-next
1457    /// Fetch a [`GStrInteriorNulError`] to get more details about the conversion failure.
1458    #[inline]
1459    pub fn nul_error(&self) -> GStrInteriorNulError {
1460        self.1
1461    }
1462}
1463
1464// rustdoc-stripper-ignore-next
1465/// Error type indicating that a buffer had invalid UTF-8.
1466///
1467/// `T` is the type of the value the conversion was attempted from.
1468#[derive(Clone, PartialEq, Eq, Debug)]
1469pub struct GStringUtf8Error<T>(T, std::str::Utf8Error);
1470
1471impl<T> std::error::Error for GStringUtf8Error<T> where T: fmt::Debug {}
1472
1473impl<T> fmt::Display for GStringUtf8Error<T> {
1474    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
1475        fmt::Display::fmt(&self.1, fmt)
1476    }
1477}
1478
1479impl<T> GStringUtf8Error<T> {
1480    // rustdoc-stripper-ignore-next
1481    /// Returns the original value that was attempted to convert to [`GString`].
1482    #[inline]
1483    pub fn into_inner(self) -> T {
1484        self.0
1485    }
1486    // rustdoc-stripper-ignore-next
1487    /// Fetch a [`Utf8Error`](std::str::Utf8Error) to get more details about the conversion
1488    /// failure.
1489    #[inline]
1490    pub fn utf8_error(&self) -> std::str::Utf8Error {
1491        self.1
1492    }
1493}
1494
1495// rustdoc-stripper-ignore-next
1496/// Error type holding all possible failures when creating a [`GString`].
1497#[derive(Debug)]
1498pub enum GStringFromError<T> {
1499    NoTrailingNul(GStringNoTrailingNulError<T>),
1500    InteriorNul(GStringInteriorNulError<T>),
1501    InvalidUtf8(GStringUtf8Error<T>),
1502    Unspecified(T),
1503}
1504
1505impl<T> std::error::Error for GStringFromError<T>
1506where
1507    T: fmt::Debug,
1508{
1509    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
1510        match self {
1511            Self::NoTrailingNul(err) => std::error::Error::source(err),
1512            Self::InteriorNul(err) => std::error::Error::source(err),
1513            Self::InvalidUtf8(err) => std::error::Error::source(err),
1514            Self::Unspecified { .. } => None,
1515        }
1516    }
1517}
1518
1519impl<T> fmt::Display for GStringFromError<T> {
1520    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
1521        match self {
1522            Self::NoTrailingNul(err) => fmt::Display::fmt(err, fmt),
1523            Self::InteriorNul(err) => fmt::Display::fmt(err, fmt),
1524            Self::InvalidUtf8(err) => fmt::Display::fmt(err, fmt),
1525            Self::Unspecified(_) => fmt.write_str("unable to convert"),
1526        }
1527    }
1528}
1529
1530impl<T> std::convert::From<GStringNoTrailingNulError<T>> for GStringFromError<T> {
1531    fn from(err: GStringNoTrailingNulError<T>) -> Self {
1532        GStringFromError::NoTrailingNul(err)
1533    }
1534}
1535
1536impl<T> std::convert::From<GStringInteriorNulError<T>> for GStringFromError<T> {
1537    fn from(err: GStringInteriorNulError<T>) -> Self {
1538        GStringFromError::InteriorNul(err)
1539    }
1540}
1541
1542impl<T> std::convert::From<GStringUtf8Error<T>> for GStringFromError<T> {
1543    fn from(err: GStringUtf8Error<T>) -> Self {
1544        GStringFromError::InvalidUtf8(err)
1545    }
1546}
1547
1548impl<T> GStringFromError<T> {
1549    pub fn into_inner(self) -> T {
1550        match self {
1551            Self::NoTrailingNul(GStringNoTrailingNulError(t)) => t,
1552            Self::InteriorNul(GStringInteriorNulError(t, _)) => t,
1553            Self::InvalidUtf8(GStringUtf8Error(t, _)) => t,
1554            Self::Unspecified(t) => t,
1555        }
1556    }
1557    #[inline]
1558    fn convert<R>(self, func: impl FnOnce(T) -> R) -> GStringFromError<R> {
1559        match self {
1560            Self::NoTrailingNul(GStringNoTrailingNulError(t)) => {
1561                GStringFromError::NoTrailingNul(GStringNoTrailingNulError(func(t)))
1562            }
1563            Self::InteriorNul(GStringInteriorNulError(t, e)) => {
1564                GStringFromError::InteriorNul(GStringInteriorNulError(func(t), e))
1565            }
1566            Self::InvalidUtf8(GStringUtf8Error(t, e)) => {
1567                GStringFromError::InvalidUtf8(GStringUtf8Error(func(t), e))
1568            }
1569            Self::Unspecified(t) => GStringFromError::Unspecified(func(t)),
1570        }
1571    }
1572}
1573
1574impl From<std::string::FromUtf8Error> for GStringFromError<Vec<u8>> {
1575    #[inline]
1576    fn from(e: std::string::FromUtf8Error) -> Self {
1577        let ue = e.utf8_error();
1578        Self::InvalidUtf8(GStringUtf8Error(e.into_bytes(), ue))
1579    }
1580}
1581
1582impl IntoGlibPtr<*mut c_char> for GString {
1583    // rustdoc-stripper-ignore-next
1584    /// Transform into a nul-terminated raw C string pointer.
1585    #[inline]
1586    fn into_glib_ptr(self) -> *mut c_char {
1587        match self.0 {
1588            Inner::Native(ref s) => unsafe { ffi::g_strndup(s.as_ptr() as *const _, s.len()) },
1589            Inner::Foreign { ptr, .. } => {
1590                let _s = mem::ManuallyDrop::new(self);
1591                ptr.as_ptr()
1592            }
1593            Inner::Inline { len, ref data } => unsafe {
1594                ffi::g_strndup(data.as_ptr() as *const _, len as usize)
1595            },
1596        }
1597    }
1598}
1599
1600impl Default for GString {
1601    #[inline]
1602    fn default() -> Self {
1603        Self::new()
1604    }
1605}
1606
1607impl Clone for GString {
1608    #[inline]
1609    fn clone(&self) -> GString {
1610        self.as_str().into()
1611    }
1612}
1613
1614impl fmt::Debug for GString {
1615    #[inline]
1616    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1617        <&str as fmt::Debug>::fmt(&self.as_str(), f)
1618    }
1619}
1620
1621impl Drop for GString {
1622    #[inline]
1623    fn drop(&mut self) {
1624        if let Inner::Foreign { ptr, .. } = self.0 {
1625            unsafe {
1626                ffi::g_free(ptr.as_ptr() as *mut _);
1627            }
1628        }
1629    }
1630}
1631
1632impl fmt::Display for GString {
1633    #[inline]
1634    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1635        f.write_str(self.as_str())
1636    }
1637}
1638
1639impl hash::Hash for GString {
1640    #[inline]
1641    fn hash<H: hash::Hasher>(&self, state: &mut H) {
1642        self.as_str().hash(state)
1643    }
1644}
1645
1646impl Borrow<GStr> for GString {
1647    #[inline]
1648    fn borrow(&self) -> &GStr {
1649        self.as_gstr()
1650    }
1651}
1652
1653impl Borrow<str> for GString {
1654    #[inline]
1655    fn borrow(&self) -> &str {
1656        self.as_str()
1657    }
1658}
1659
1660impl Ord for GString {
1661    #[inline]
1662    fn cmp(&self, other: &GString) -> Ordering {
1663        self.as_str().cmp(other.as_str())
1664    }
1665}
1666
1667impl PartialOrd for GString {
1668    #[inline]
1669    fn partial_cmp(&self, other: &GString) -> Option<Ordering> {
1670        Some(self.cmp(other))
1671    }
1672}
1673
1674impl PartialEq for GString {
1675    #[inline]
1676    fn eq(&self, other: &GString) -> bool {
1677        self.as_str() == other.as_str()
1678    }
1679}
1680
1681impl PartialEq<GString> for String {
1682    #[inline]
1683    fn eq(&self, other: &GString) -> bool {
1684        self.as_str() == other.as_str()
1685    }
1686}
1687
1688impl PartialEq<GStr> for GString {
1689    #[inline]
1690    fn eq(&self, other: &GStr) -> bool {
1691        self.as_str() == other.as_str()
1692    }
1693}
1694
1695impl PartialEq<&GStr> for GString {
1696    #[inline]
1697    fn eq(&self, other: &&GStr) -> bool {
1698        self.as_str() == other.as_str()
1699    }
1700}
1701
1702impl PartialEq<str> for GString {
1703    #[inline]
1704    fn eq(&self, other: &str) -> bool {
1705        self.as_str() == other
1706    }
1707}
1708
1709impl PartialEq<&str> for GString {
1710    #[inline]
1711    fn eq(&self, other: &&str) -> bool {
1712        self.as_str() == *other
1713    }
1714}
1715
1716impl PartialEq<GString> for &GStr {
1717    #[inline]
1718    fn eq(&self, other: &GString) -> bool {
1719        self.as_str() == other.as_str()
1720    }
1721}
1722
1723impl PartialEq<GString> for &str {
1724    #[inline]
1725    fn eq(&self, other: &GString) -> bool {
1726        *self == other.as_str()
1727    }
1728}
1729
1730impl PartialEq<String> for GString {
1731    #[inline]
1732    fn eq(&self, other: &String) -> bool {
1733        self.as_str() == other.as_str()
1734    }
1735}
1736
1737impl PartialEq<GString> for str {
1738    #[inline]
1739    fn eq(&self, other: &GString) -> bool {
1740        self == other.as_str()
1741    }
1742}
1743
1744impl PartialEq<GString> for GStr {
1745    #[inline]
1746    fn eq(&self, other: &GString) -> bool {
1747        self.as_str() == other.as_str()
1748    }
1749}
1750
1751impl PartialOrd<GString> for String {
1752    #[inline]
1753    fn partial_cmp(&self, other: &GString) -> Option<Ordering> {
1754        Some(self.cmp(&String::from(other.as_str())))
1755    }
1756}
1757
1758impl PartialOrd<String> for GString {
1759    #[inline]
1760    fn partial_cmp(&self, other: &String) -> Option<Ordering> {
1761        Some(self.as_str().cmp(other.as_str()))
1762    }
1763}
1764
1765impl PartialOrd<GString> for GStr {
1766    #[inline]
1767    fn partial_cmp(&self, other: &GString) -> Option<Ordering> {
1768        Some(self.as_str().cmp(other))
1769    }
1770}
1771
1772impl PartialOrd<GStr> for GString {
1773    #[inline]
1774    fn partial_cmp(&self, other: &GStr) -> Option<Ordering> {
1775        Some(self.as_str().cmp(other.as_str()))
1776    }
1777}
1778
1779impl PartialOrd<GString> for str {
1780    #[inline]
1781    fn partial_cmp(&self, other: &GString) -> Option<Ordering> {
1782        Some(self.cmp(other))
1783    }
1784}
1785
1786impl PartialOrd<str> for GString {
1787    #[inline]
1788    fn partial_cmp(&self, other: &str) -> Option<Ordering> {
1789        Some(self.as_str().cmp(other))
1790    }
1791}
1792
1793impl Eq for GString {}
1794
1795impl AsRef<GStr> for GString {
1796    #[inline]
1797    fn as_ref(&self) -> &GStr {
1798        self.as_gstr()
1799    }
1800}
1801
1802impl AsRef<str> for GString {
1803    #[inline]
1804    fn as_ref(&self) -> &str {
1805        self.as_str()
1806    }
1807}
1808
1809impl AsRef<OsStr> for GString {
1810    #[inline]
1811    fn as_ref(&self) -> &OsStr {
1812        OsStr::new(self.as_str())
1813    }
1814}
1815
1816impl AsRef<Path> for GString {
1817    #[inline]
1818    fn as_ref(&self) -> &Path {
1819        Path::new(self.as_str())
1820    }
1821}
1822
1823impl AsRef<[u8]> for GString {
1824    #[inline]
1825    fn as_ref(&self) -> &[u8] {
1826        self.as_str().as_bytes()
1827    }
1828}
1829
1830impl Deref for GString {
1831    type Target = str;
1832
1833    #[inline]
1834    fn deref(&self) -> &str {
1835        self.as_str()
1836    }
1837}
1838
1839impl From<GString> for String {
1840    #[inline]
1841    fn from(mut s: GString) -> Self {
1842        match &mut s.0 {
1843            Inner::Native(s) => {
1844                // Moves the underlying string
1845                let mut s = String::from(mem::replace(s, "".into()));
1846                let _nul = s.pop();
1847                debug_assert_eq!(_nul, Some('\0'));
1848                s
1849            }
1850            Inner::Foreign { len, .. } if *len == 0 => String::new(),
1851            Inner::Foreign { ptr, len } => unsafe {
1852                // Creates a copy
1853                let slice = slice::from_raw_parts(ptr.as_ptr() as *const u8, *len);
1854                std::str::from_utf8_unchecked(slice).into()
1855            },
1856            Inner::Inline { len, data } => unsafe {
1857                std::str::from_utf8_unchecked(data.get_unchecked(..*len as usize)).to_owned()
1858            },
1859        }
1860    }
1861}
1862
1863impl From<GString> for Box<str> {
1864    #[inline]
1865    fn from(s: GString) -> Self {
1866        // Potentially creates a copy
1867        String::from(s).into()
1868    }
1869}
1870
1871impl From<GString> for Vec<u8> {
1872    #[inline]
1873    fn from(value: GString) -> Vec<u8> {
1874        value.into_bytes_with_nul()
1875    }
1876}
1877
1878impl TryFrom<GString> for CString {
1879    type Error = GStringInteriorNulError<GString>;
1880    #[inline]
1881    fn try_from(value: GString) -> Result<Self, Self::Error> {
1882        if let Some(nul_pos) = memchr::memchr(0, value.as_bytes()) {
1883            return Err(GStringInteriorNulError(
1884                value,
1885                GStrInteriorNulError(nul_pos),
1886            ));
1887        }
1888        let v = value.into_bytes_with_nul();
1889        Ok(unsafe { CString::from_vec_with_nul_unchecked(v) })
1890    }
1891}
1892
1893impl From<GString> for OsString {
1894    #[inline]
1895    fn from(s: GString) -> Self {
1896        OsString::from(String::from(s))
1897    }
1898}
1899
1900impl From<GString> for PathBuf {
1901    #[inline]
1902    fn from(s: GString) -> Self {
1903        PathBuf::from(OsString::from(s))
1904    }
1905}
1906
1907impl From<String> for GString {
1908    #[inline]
1909    fn from(mut s: String) -> Self {
1910        // Moves the content of the String
1911        if cfg!(debug_assertions) {
1912            GStr::check_interior_nuls(&s).unwrap();
1913        }
1914        if s.is_empty() {
1915            Self::new()
1916        } else {
1917            s.reserve_exact(1);
1918            s.push('\0');
1919            // No check for valid UTF-8 here
1920            Self(Inner::Native(s.into()))
1921        }
1922    }
1923}
1924
1925impl From<Box<str>> for GString {
1926    #[inline]
1927    fn from(s: Box<str>) -> Self {
1928        // Moves the content of the String
1929        s.into_string().into()
1930    }
1931}
1932
1933impl<'a> From<Cow<'a, str>> for GString {
1934    #[inline]
1935    fn from(s: Cow<'a, str>) -> Self {
1936        match s {
1937            Cow::Borrowed(s) => Self::from(s),
1938            Cow::Owned(s) => Self::from(s),
1939        }
1940    }
1941}
1942
1943impl From<&GStr> for GString {
1944    #[inline]
1945    fn from(s: &GStr) -> GString {
1946        s.to_owned()
1947    }
1948}
1949
1950impl From<&str> for GString {
1951    #[inline]
1952    fn from(s: &str) -> Self {
1953        if cfg!(debug_assertions) {
1954            GStr::check_interior_nuls(s).unwrap();
1955        }
1956        if s.len() < INLINE_LEN {
1957            let mut data = <[u8; INLINE_LEN]>::default();
1958            let b = s.as_bytes();
1959            unsafe { data.get_unchecked_mut(..b.len()) }.copy_from_slice(b);
1960            return Self(Inner::Inline {
1961                len: b.len() as u8,
1962                data,
1963            });
1964        }
1965        // Allocates with the GLib allocator
1966        unsafe {
1967            // No check for valid UTF-8 here
1968            let copy = ffi::g_strndup(s.as_ptr() as *const c_char, s.len());
1969            GString(Inner::Foreign {
1970                ptr: ptr::NonNull::new_unchecked(copy),
1971                len: s.len(),
1972            })
1973        }
1974    }
1975}
1976
1977impl From<&String> for GString {
1978    #[inline]
1979    fn from(s: &String) -> Self {
1980        GString::from(s.as_str())
1981    }
1982}
1983
1984impl From<GStringPtr> for GString {
1985    #[inline]
1986    fn from(s: GStringPtr) -> Self {
1987        let s = mem::ManuallyDrop::new(s);
1988        let len = unsafe { GStr::from_ptr(s.0.as_ptr()).len() };
1989        GString(Inner::Foreign { ptr: s.0, len })
1990    }
1991}
1992
1993impl TryFrom<CString> for GString {
1994    type Error = GStringUtf8Error<CString>;
1995    #[inline]
1996    fn try_from(value: CString) -> Result<Self, Self::Error> {
1997        if value.as_bytes().is_empty() {
1998            Ok(Self::new())
1999        } else {
2000            // Moves the content of the CString
2001            // Also check if it's valid UTF-8
2002            let s = String::from_utf8(value.into_bytes_with_nul()).map_err(|e| {
2003                let err = e.utf8_error();
2004                GStringUtf8Error(
2005                    unsafe { CString::from_vec_with_nul_unchecked(e.into_bytes()) },
2006                    err,
2007                )
2008            })?;
2009            Ok(Self(Inner::Native(s.into())))
2010        }
2011    }
2012}
2013
2014impl TryFrom<OsString> for GString {
2015    type Error = GStringFromError<OsString>;
2016    #[inline]
2017    fn try_from(value: OsString) -> Result<Self, Self::Error> {
2018        Self::from_string_checked(value.into_string().map_err(GStringFromError::Unspecified)?)
2019            .map_err(|e| GStringFromError::from(e).convert(OsString::from))
2020    }
2021}
2022
2023impl TryFrom<PathBuf> for GString {
2024    type Error = GStringFromError<PathBuf>;
2025    #[inline]
2026    fn try_from(value: PathBuf) -> Result<Self, Self::Error> {
2027        GString::try_from(value.into_os_string()).map_err(|e| e.convert(PathBuf::from))
2028    }
2029}
2030
2031impl TryFrom<&CStr> for GString {
2032    type Error = std::str::Utf8Error;
2033    #[inline]
2034    fn try_from(value: &CStr) -> Result<Self, Self::Error> {
2035        // Check if it's valid UTF-8
2036        value.to_str()?;
2037        let gstr = unsafe { GStr::from_utf8_with_nul_unchecked(value.to_bytes_with_nul()) };
2038        Ok(gstr.to_owned())
2039    }
2040}
2041
2042impl<'a> From<Cow<'a, GStr>> for GString {
2043    #[inline]
2044    fn from(s: Cow<'a, GStr>) -> Self {
2045        s.into_owned()
2046    }
2047}
2048
2049impl<'a> From<&'a GString> for Cow<'a, GStr> {
2050    #[inline]
2051    fn from(s: &'a GString) -> Self {
2052        Cow::Borrowed(s.as_gstr())
2053    }
2054}
2055
2056impl From<GString> for Cow<'_, GStr> {
2057    #[inline]
2058    fn from(v: GString) -> Self {
2059        Cow::Owned(v)
2060    }
2061}
2062
2063impl<'a> From<&'a GStr> for Cow<'a, GStr> {
2064    #[inline]
2065    fn from(v: &'a GStr) -> Self {
2066        Cow::Borrowed(v)
2067    }
2068}
2069
2070#[doc(hidden)]
2071impl FromGlibPtrFull<*mut u8> for GString {
2072    #[inline]
2073    unsafe fn from_glib_full(ptr: *mut u8) -> Self {
2074        unsafe {
2075            debug_assert!(!ptr.is_null());
2076
2077            let cstr = CStr::from_ptr(ptr as *const _);
2078            // Check for valid UTF-8 here
2079            debug_assert!(cstr.to_str().is_ok());
2080            Self(Inner::Foreign {
2081                ptr: ptr::NonNull::new_unchecked(ptr as *mut _),
2082                len: cstr.to_bytes().len(),
2083            })
2084        }
2085    }
2086}
2087
2088#[doc(hidden)]
2089impl FromGlibPtrFull<*mut i8> for GString {
2090    #[inline]
2091    unsafe fn from_glib_full(ptr: *mut i8) -> Self {
2092        unsafe { from_glib_full(ptr as *mut u8) }
2093    }
2094}
2095
2096#[doc(hidden)]
2097impl FromGlibPtrFull<*const u8> for GString {
2098    #[inline]
2099    unsafe fn from_glib_full(ptr: *const u8) -> Self {
2100        unsafe { from_glib_full(ptr as *mut u8) }
2101    }
2102}
2103
2104#[doc(hidden)]
2105impl FromGlibPtrFull<*const i8> for GString {
2106    #[inline]
2107    unsafe fn from_glib_full(ptr: *const i8) -> Self {
2108        unsafe { from_glib_full(ptr as *mut u8) }
2109    }
2110}
2111
2112#[doc(hidden)]
2113impl FromGlibPtrNone<*const u8> for GString {
2114    #[inline]
2115    unsafe fn from_glib_none(ptr: *const u8) -> Self {
2116        unsafe {
2117            debug_assert!(!ptr.is_null());
2118            <&GStr>::from_glib_none(ptr).into()
2119        }
2120    }
2121}
2122
2123#[doc(hidden)]
2124impl FromGlibPtrNone<*const i8> for GString {
2125    #[inline]
2126    unsafe fn from_glib_none(ptr: *const i8) -> Self {
2127        unsafe { from_glib_none(ptr as *const u8) }
2128    }
2129}
2130
2131#[doc(hidden)]
2132impl FromGlibPtrNone<*mut u8> for GString {
2133    #[inline]
2134    unsafe fn from_glib_none(ptr: *mut u8) -> Self {
2135        unsafe { from_glib_none(ptr as *const u8) }
2136    }
2137}
2138
2139#[doc(hidden)]
2140impl FromGlibPtrNone<*mut i8> for GString {
2141    #[inline]
2142    unsafe fn from_glib_none(ptr: *mut i8) -> Self {
2143        unsafe { from_glib_none(ptr as *const u8) }
2144    }
2145}
2146
2147#[doc(hidden)]
2148impl FromGlibPtrBorrow<*const u8> for GString {
2149    #[inline]
2150    unsafe fn from_glib_borrow(ptr: *const u8) -> Borrowed<Self> {
2151        unsafe {
2152            debug_assert!(!ptr.is_null());
2153
2154            // Check for valid UTF-8 here
2155            let cstr = CStr::from_ptr(ptr as *const _);
2156            debug_assert!(cstr.to_str().is_ok());
2157            Borrowed::new(Self(Inner::Foreign {
2158                ptr: ptr::NonNull::new_unchecked(ptr as *mut _),
2159                len: cstr.to_bytes().len(),
2160            }))
2161        }
2162    }
2163}
2164
2165#[doc(hidden)]
2166impl FromGlibPtrBorrow<*const i8> for GString {
2167    #[inline]
2168    unsafe fn from_glib_borrow(ptr: *const i8) -> Borrowed<Self> {
2169        unsafe { from_glib_borrow(ptr as *const u8) }
2170    }
2171}
2172
2173#[doc(hidden)]
2174impl FromGlibPtrBorrow<*mut u8> for GString {
2175    #[inline]
2176    unsafe fn from_glib_borrow(ptr: *mut u8) -> Borrowed<Self> {
2177        unsafe { from_glib_borrow(ptr as *const u8) }
2178    }
2179}
2180
2181#[doc(hidden)]
2182impl FromGlibPtrBorrow<*mut i8> for GString {
2183    #[inline]
2184    unsafe fn from_glib_borrow(ptr: *mut i8) -> Borrowed<Self> {
2185        unsafe { from_glib_borrow(ptr as *const u8) }
2186    }
2187}
2188
2189#[allow(clippy::unnecessary_cast)]
2190#[doc(hidden)]
2191impl<'a> ToGlibPtr<'a, *const u8> for GString {
2192    type Storage = PhantomData<&'a Self>;
2193
2194    #[inline]
2195    fn to_glib_none(&'a self) -> Stash<'a, *const u8, Self> {
2196        Stash(self.as_ptr() as *const u8, PhantomData)
2197    }
2198
2199    #[inline]
2200    fn to_glib_full(&self) -> *const u8 {
2201        self.clone().into_glib_ptr() as *const u8
2202    }
2203}
2204
2205#[allow(clippy::unnecessary_cast)]
2206#[doc(hidden)]
2207impl<'a> ToGlibPtr<'a, *const i8> for GString {
2208    type Storage = PhantomData<&'a Self>;
2209
2210    #[inline]
2211    fn to_glib_none(&'a self) -> Stash<'a, *const i8, Self> {
2212        Stash(self.as_ptr() as *const i8, PhantomData)
2213    }
2214
2215    #[inline]
2216    fn to_glib_full(&self) -> *const i8 {
2217        self.clone().into_glib_ptr() as *const i8
2218    }
2219}
2220
2221#[allow(clippy::unnecessary_cast)]
2222#[doc(hidden)]
2223impl<'a> ToGlibPtr<'a, *mut u8> for GString {
2224    type Storage = PhantomData<&'a Self>;
2225
2226    #[inline]
2227    fn to_glib_none(&'a self) -> Stash<'a, *mut u8, Self> {
2228        Stash(self.as_ptr() as *mut u8, PhantomData)
2229    }
2230
2231    #[inline]
2232    fn to_glib_full(&self) -> *mut u8 {
2233        self.clone().into_glib_ptr() as *mut u8
2234    }
2235}
2236
2237#[allow(clippy::unnecessary_cast)]
2238#[doc(hidden)]
2239impl<'a> ToGlibPtr<'a, *mut i8> for GString {
2240    type Storage = PhantomData<&'a Self>;
2241
2242    #[inline]
2243    fn to_glib_none(&'a self) -> Stash<'a, *mut i8, Self> {
2244        Stash(self.as_ptr() as *mut i8, PhantomData)
2245    }
2246
2247    #[inline]
2248    fn to_glib_full(&self) -> *mut i8 {
2249        self.clone().into_glib_ptr() as *mut i8
2250    }
2251}
2252
2253#[doc(hidden)]
2254impl FromGlibContainer<*const c_char, *const i8> for GString {
2255    unsafe fn from_glib_none_num(ptr: *const i8, num: usize) -> Self {
2256        unsafe {
2257            if num == 0 || ptr.is_null() {
2258                return Self::default();
2259            }
2260            let slice = slice::from_raw_parts(ptr as *const u8, num);
2261            if cfg!(debug_assertions) {
2262                // Also check if it's valid UTF-8
2263                std::str::from_utf8(slice).unwrap().into()
2264            } else {
2265                std::str::from_utf8_unchecked(slice).into()
2266            }
2267        }
2268    }
2269
2270    unsafe fn from_glib_container_num(ptr: *const i8, num: usize) -> Self {
2271        unsafe {
2272            if num == 0 || ptr.is_null() {
2273                return Self::default();
2274            }
2275
2276            if cfg!(debug_assertions) {
2277                // Check if it's valid UTF-8
2278                let slice = slice::from_raw_parts(ptr as *const u8, num);
2279                std::str::from_utf8(slice).unwrap();
2280            }
2281
2282            GString(Inner::Foreign {
2283                ptr: ptr::NonNull::new_unchecked(ptr as *mut _),
2284                len: num,
2285            })
2286        }
2287    }
2288
2289    unsafe fn from_glib_full_num(ptr: *const i8, num: usize) -> Self {
2290        unsafe {
2291            if num == 0 || ptr.is_null() {
2292                return Self::default();
2293            }
2294
2295            if cfg!(debug_assertions) {
2296                // Check if it's valid UTF-8
2297                let slice = slice::from_raw_parts(ptr as *const u8, num);
2298                std::str::from_utf8(slice).unwrap();
2299            }
2300
2301            GString(Inner::Foreign {
2302                ptr: ptr::NonNull::new_unchecked(ptr as *mut _),
2303                len: num,
2304            })
2305        }
2306    }
2307}
2308
2309#[doc(hidden)]
2310impl FromGlibContainer<*const c_char, *mut i8> for GString {
2311    unsafe fn from_glib_none_num(ptr: *mut i8, num: usize) -> Self {
2312        unsafe { FromGlibContainer::from_glib_none_num(ptr as *const i8, num) }
2313    }
2314
2315    unsafe fn from_glib_container_num(ptr: *mut i8, num: usize) -> Self {
2316        unsafe { FromGlibContainer::from_glib_container_num(ptr as *const i8, num) }
2317    }
2318
2319    unsafe fn from_glib_full_num(ptr: *mut i8, num: usize) -> Self {
2320        unsafe { FromGlibContainer::from_glib_full_num(ptr as *const i8, num) }
2321    }
2322}
2323
2324#[doc(hidden)]
2325impl FromGlibContainer<*const c_char, *const u8> for GString {
2326    unsafe fn from_glib_none_num(ptr: *const u8, num: usize) -> Self {
2327        unsafe { FromGlibContainer::from_glib_none_num(ptr as *const i8, num) }
2328    }
2329
2330    unsafe fn from_glib_container_num(ptr: *const u8, num: usize) -> Self {
2331        unsafe { FromGlibContainer::from_glib_container_num(ptr as *const i8, num) }
2332    }
2333
2334    unsafe fn from_glib_full_num(ptr: *const u8, num: usize) -> Self {
2335        unsafe { FromGlibContainer::from_glib_full_num(ptr as *const i8, num) }
2336    }
2337}
2338
2339#[doc(hidden)]
2340impl FromGlibContainer<*const c_char, *mut u8> for GString {
2341    unsafe fn from_glib_none_num(ptr: *mut u8, num: usize) -> Self {
2342        unsafe { FromGlibContainer::from_glib_none_num(ptr as *const i8, num) }
2343    }
2344
2345    unsafe fn from_glib_container_num(ptr: *mut u8, num: usize) -> Self {
2346        unsafe { FromGlibContainer::from_glib_container_num(ptr as *const i8, num) }
2347    }
2348
2349    unsafe fn from_glib_full_num(ptr: *mut u8, num: usize) -> Self {
2350        unsafe { FromGlibContainer::from_glib_full_num(ptr as *const i8, num) }
2351    }
2352}
2353
2354impl GlibPtrDefault for GString {
2355    type GlibType = *const c_char;
2356}
2357
2358impl StaticType for GString {
2359    #[inline]
2360    fn static_type() -> Type {
2361        String::static_type()
2362    }
2363}
2364
2365impl crate::value::ValueType for GString {
2366    type Type = String;
2367}
2368
2369impl crate::value::ValueTypeOptional for GString {}
2370
2371unsafe impl<'a> crate::value::FromValue<'a> for GString {
2372    type Checker = crate::value::GenericValueTypeOrNoneChecker<Self>;
2373
2374    #[inline]
2375    unsafe fn from_value(value: &'a Value) -> Self {
2376        unsafe { Self::from(<&str>::from_value(value)) }
2377    }
2378}
2379
2380impl crate::value::ToValue for GString {
2381    #[inline]
2382    fn to_value(&self) -> Value {
2383        <&str>::to_value(&self.as_str())
2384    }
2385
2386    #[inline]
2387    fn value_type(&self) -> Type {
2388        String::static_type()
2389    }
2390}
2391
2392impl crate::value::ToValueOptional for GString {
2393    #[inline]
2394    fn to_value_optional(s: Option<&Self>) -> Value {
2395        <str>::to_value_optional(s.as_ref().map(|s| s.as_str()))
2396    }
2397}
2398
2399impl From<GString> for Value {
2400    #[inline]
2401    fn from(s: GString) -> Self {
2402        unsafe {
2403            let mut value = Value::for_value_type::<GString>();
2404            gobject_ffi::g_value_take_string(value.to_glib_none_mut().0, s.into_glib_ptr());
2405            value
2406        }
2407    }
2408}
2409
2410impl StaticType for Vec<GString> {
2411    #[inline]
2412    fn static_type() -> Type {
2413        <Vec<String>>::static_type()
2414    }
2415}
2416
2417impl crate::value::ValueType for Vec<GString> {
2418    type Type = Vec<GString>;
2419}
2420
2421unsafe impl<'a> crate::value::FromValue<'a> for Vec<GString> {
2422    type Checker = crate::value::GenericValueTypeChecker<Self>;
2423
2424    #[inline]
2425    unsafe fn from_value(value: &'a Value) -> Self {
2426        unsafe {
2427            let ptr =
2428                gobject_ffi::g_value_get_boxed(value.to_glib_none().0) as *const *const c_char;
2429            FromGlibPtrContainer::from_glib_none(ptr)
2430        }
2431    }
2432}
2433
2434impl ToValue for Vec<GString> {
2435    #[inline]
2436    fn to_value(&self) -> Value {
2437        unsafe {
2438            let mut value = Value::for_value_type::<Self>();
2439            let ptr: *mut *mut c_char = self.to_glib_full();
2440            gobject_ffi::g_value_take_boxed(value.to_glib_none_mut().0, ptr as *const c_void);
2441            value
2442        }
2443    }
2444
2445    #[inline]
2446    fn value_type(&self) -> Type {
2447        <Vec<GString>>::static_type()
2448    }
2449}
2450
2451impl From<Vec<GString>> for Value {
2452    #[inline]
2453    fn from(v: Vec<GString>) -> Self {
2454        unsafe {
2455            let v_ptr =
2456                ffi::g_malloc(mem::size_of::<*mut c_char>() * (v.len() + 1)) as *mut *mut c_char;
2457            v_ptr.add(v.len()).write(ptr::null_mut());
2458            for (i, s) in v.into_iter().enumerate() {
2459                v_ptr.add(i).write(s.into_glib_ptr());
2460            }
2461
2462            let mut value = Value::for_value_type::<Vec<GString>>();
2463            gobject_ffi::g_value_take_boxed(value.to_glib_none_mut().0, v_ptr as *const c_void);
2464            value
2465        }
2466    }
2467}
2468
2469impl_from_glib_container_as_vec_string!(GString, *const c_char);
2470impl_from_glib_container_as_vec_string!(GString, *mut c_char);
2471
2472// rustdoc-stripper-ignore-next
2473/// A trait to accept both <code>&[str]</code> or <code>&[GStr]</code> as an argument.
2474pub trait IntoGStr {
2475    fn run_with_gstr<T, F: FnOnce(&GStr) -> T>(self, f: F) -> T;
2476}
2477
2478impl IntoGStr for &GStr {
2479    #[inline]
2480    fn run_with_gstr<T, F: FnOnce(&GStr) -> T>(self, f: F) -> T {
2481        f(self)
2482    }
2483}
2484
2485impl IntoGStr for GString {
2486    #[inline]
2487    fn run_with_gstr<T, F: FnOnce(&GStr) -> T>(self, f: F) -> T {
2488        f(self.as_gstr())
2489    }
2490}
2491
2492impl IntoGStr for &GString {
2493    #[inline]
2494    fn run_with_gstr<T, F: FnOnce(&GStr) -> T>(self, f: F) -> T {
2495        f(self.as_gstr())
2496    }
2497}
2498
2499// Limit borrowed from rust std CStr optimization:
2500// https://github.com/rust-lang/rust/blob/master/library/std/src/sys/common/small_c_string.rs#L10
2501const MAX_STACK_ALLOCATION: usize = 384;
2502
2503impl IntoGStr for &str {
2504    #[inline]
2505    fn run_with_gstr<T, F: FnOnce(&GStr) -> T>(self, f: F) -> T {
2506        if self.len() < MAX_STACK_ALLOCATION {
2507            let mut s = mem::MaybeUninit::<[u8; MAX_STACK_ALLOCATION]>::uninit();
2508            let ptr = s.as_mut_ptr() as *mut u8;
2509            let gs = unsafe {
2510                ptr::copy_nonoverlapping(self.as_ptr(), ptr, self.len());
2511                ptr.add(self.len()).write(0);
2512                GStr::from_utf8_with_nul_unchecked(slice::from_raw_parts(ptr, self.len() + 1))
2513            };
2514            f(gs)
2515        } else {
2516            f(GString::from(self).as_gstr())
2517        }
2518    }
2519}
2520
2521impl IntoGStr for String {
2522    #[inline]
2523    fn run_with_gstr<T, F: FnOnce(&GStr) -> T>(mut self, f: F) -> T {
2524        let len = self.len();
2525        if len < self.capacity() {
2526            self.reserve_exact(1);
2527            self.push('\0');
2528            let gs = unsafe { GStr::from_utf8_with_nul_unchecked(self.as_bytes()) };
2529            f(gs)
2530        } else if len < MAX_STACK_ALLOCATION {
2531            self.as_str().run_with_gstr(f)
2532        } else {
2533            f(GString::from(self).as_gstr())
2534        }
2535    }
2536}
2537
2538impl IntoGStr for &String {
2539    #[inline]
2540    fn run_with_gstr<T, F: FnOnce(&GStr) -> T>(self, f: F) -> T {
2541        self.as_str().run_with_gstr(f)
2542    }
2543}
2544
2545pub const NONE_STR: Option<&'static str> = None;
2546
2547// rustdoc-stripper-ignore-next
2548/// A trait to accept both <code>[Option]&lt;&[str]></code> or <code>[Option]&lt;&[GStr]></code> as
2549/// an argument.
2550pub trait IntoOptionalGStr {
2551    fn run_with_gstr<T, F: FnOnce(Option<&GStr>) -> T>(self, f: F) -> T;
2552}
2553
2554impl<S: IntoGStr> IntoOptionalGStr for Option<S> {
2555    #[inline]
2556    fn run_with_gstr<T, F: FnOnce(Option<&GStr>) -> T>(self, f: F) -> T {
2557        match self {
2558            Some(t) => t.run_with_gstr(|s| f(Some(s))),
2559            None => f(None),
2560        }
2561    }
2562}
2563
2564#[cfg(test)]
2565#[allow(clippy::disallowed_names)]
2566mod tests {
2567    use std::ffi::CString;
2568
2569    use super::*;
2570
2571    #[test]
2572    fn test_gstring() {
2573        let data = CString::new("foo").unwrap();
2574        let ptr = data.as_ptr();
2575
2576        unsafe {
2577            let ptr_copy = ffi::g_strdup(ptr);
2578            let gstring = GString::from_glib_full(ptr_copy);
2579            assert_eq!(gstring.as_str(), "foo");
2580            let foo: Box<str> = gstring.into();
2581            assert_eq!(foo.as_ref(), "foo");
2582        }
2583    }
2584
2585    #[test]
2586    fn test_owned_glib_string() {
2587        let data = CString::new("foo").unwrap();
2588        let ptr = data.as_ptr();
2589        unsafe {
2590            let ptr_copy = ffi::g_strdup(ptr);
2591            let gstr = GString::from_glib_full(ptr_copy);
2592            assert_eq!(gstr, "foo");
2593        }
2594    }
2595
2596    #[test]
2597    fn test_gstring_from_str() {
2598        let gstring: GString = "foo".into();
2599        assert_eq!(gstring.as_str(), "foo");
2600        let foo: Box<str> = gstring.into();
2601        assert_eq!(foo.as_ref(), "foo");
2602    }
2603
2604    #[test]
2605    fn test_string_from_gstring() {
2606        let gstring = GString::from("foo");
2607        assert_eq!(gstring.as_str(), "foo");
2608        let s = String::from(gstring);
2609        assert_eq!(s, "foo");
2610    }
2611
2612    #[test]
2613    fn test_gstring_from_cstring() {
2614        let cstr = CString::new("foo").unwrap();
2615        let gstring = GString::try_from(cstr).unwrap();
2616        assert_eq!(gstring.as_str(), "foo");
2617        let foo: Box<str> = gstring.into();
2618        assert_eq!(foo.as_ref(), "foo");
2619    }
2620
2621    #[test]
2622    fn test_string_from_gstring_from_cstring() {
2623        let cstr = CString::new("foo").unwrap();
2624        let gstring = GString::try_from(cstr).unwrap();
2625        assert_eq!(gstring.as_str(), "foo");
2626        let s = String::from(gstring);
2627        assert_eq!(s, "foo");
2628    }
2629
2630    #[test]
2631    fn test_vec_u8_to_gstring() {
2632        let v: &[u8] = b"foo";
2633        let s: GString = GString::from_utf8(Vec::from(v)).unwrap();
2634        assert_eq!(s.as_str(), "foo");
2635    }
2636
2637    #[test]
2638    fn test_value_from_vec_gstring() {
2639        fn roundtrip(s: GString) {
2640            let vec = vec![s.clone()];
2641            let value = crate::Value::from(vec);
2642            let vec: Vec<GString> = value.get().unwrap();
2643            assert_eq!(vec.len(), 1);
2644            assert_eq!(s, vec[0]);
2645        }
2646
2647        roundtrip(GString::from("foo"));
2648        roundtrip(GString::from("very very very long string".to_owned()));
2649        roundtrip(GString::from(gstr!("very very very long string")));
2650    }
2651
2652    #[test]
2653    fn test_as_ref_path() {
2654        fn foo<P: AsRef<Path>>(_path: P) {}
2655        let gstring: GString = "/my/path/".into();
2656        let gstr: &GStr = gstring.as_gstr();
2657        foo(gstr);
2658        foo(gstring);
2659    }
2660
2661    #[test]
2662    fn test_from_glib_container() {
2663        unsafe {
2664            let test_a: GString = FromGlibContainer::from_glib_container_num(
2665                ffi::g_strdup("hello_world\0".as_ptr() as *const _),
2666                5,
2667            );
2668            assert_eq!("hello", test_a.as_str());
2669
2670            let test_b: GString = FromGlibContainer::from_glib_none_num("hello_world".as_ptr(), 5);
2671            assert_eq!("hello", test_b.as_str());
2672
2673            let test_c: GString =
2674                FromGlibContainer::from_glib_none_num(std::ptr::null::<std::os::raw::c_char>(), 0);
2675            assert_eq!("", test_c.as_str());
2676
2677            let test_d: GString = FromGlibContainer::from_glib_none_num("".as_ptr(), 0);
2678            assert_eq!("", test_d.as_str());
2679
2680            let test_e: GString =
2681                FromGlibContainer::from_glib_container_num(ffi::g_strdup(std::ptr::null()), 0);
2682            assert_eq!("", test_e.as_str());
2683        }
2684    }
2685
2686    #[test]
2687    fn test_hashmap() {
2688        use std::collections::HashMap;
2689
2690        let gstring = GString::from("foo");
2691        assert_eq!(gstring.as_str(), "foo");
2692        let mut h: HashMap<GString, i32> = HashMap::new();
2693        h.insert(gstring, 42);
2694        let gstring: GString = "foo".into();
2695        assert!(h.contains_key(&gstring));
2696    }
2697
2698    #[test]
2699    fn test_gstring_from_ptr_lossy() {
2700        let data = CString::new("foo").unwrap();
2701        let ptr = data.as_ptr();
2702
2703        unsafe {
2704            let gstring = GString::from_ptr_lossy(ptr);
2705            assert_eq!(gstring.as_str(), "foo");
2706            assert_eq!(ptr, gstring.as_ptr());
2707        }
2708
2709        let data = b"foo\xF0\x90\x80bar\0";
2710        let ptr = data.as_ptr();
2711
2712        unsafe {
2713            let gstring = GString::from_ptr_lossy(ptr as *const _);
2714            assert_eq!(gstring.as_str(), "foo���bar");
2715            assert_ne!(ptr, gstring.as_ptr() as *const _);
2716        }
2717    }
2718
2719    #[test]
2720    fn gformat() {
2721        let s = gformat!("bla bla {} bla", 123);
2722        assert_eq!(s, "bla bla 123 bla");
2723    }
2724
2725    #[test]
2726    fn layout() {
2727        // ensure the inline variant is not wider than the other variants
2728        enum NoInline {
2729            _Native(Box<str>),
2730            _Foreign(ptr::NonNull<c_char>, usize),
2731        }
2732        assert_eq!(mem::size_of::<GString>(), mem::size_of::<NoInline>());
2733        assert_eq!(mem::size_of::<GString>(), mem::size_of::<String>());
2734    }
2735}