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