1 | // Take a look at the license at the top of the repository in the LICENSE file. |
2 | |
3 | use 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 | |
16 | use 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)] |
24 | pub struct GStr(str); |
25 | |
26 | impl 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)] |
297 | pub enum GStrError { |
298 | InvalidUtf8(std::str::Utf8Error), |
299 | InteriorNul(GStrInteriorNulError), |
300 | NoTrailingNul, |
301 | } |
302 | |
303 | impl std::error::Error for GStrError { |
304 | fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { |
305 | match self { |
306 | Self::InvalidUtf8(err: &Utf8Error) => std::error::Error::source(self:err), |
307 | Self::InteriorNul(err: &GStrInteriorNulError) => std::error::Error::source(self:err), |
308 | Self::NoTrailingNul => None, |
309 | } |
310 | } |
311 | } |
312 | |
313 | impl fmt::Display for GStrError { |
314 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |
315 | match self { |
316 | Self::InvalidUtf8(err: &Utf8Error) => fmt::Display::fmt(self:err, f:fmt), |
317 | Self::InteriorNul(err: &GStrInteriorNulError) => fmt::Display::fmt(self:err, f:fmt), |
318 | Self::NoTrailingNul => fmt.write_str(data:"data provided is not nul terminated" ), |
319 | } |
320 | } |
321 | } |
322 | |
323 | impl std::convert::From<std::str::Utf8Error> for GStrError { |
324 | fn from(err: std::str::Utf8Error) -> Self { |
325 | Self::InvalidUtf8(err) |
326 | } |
327 | } |
328 | |
329 | impl 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)] |
338 | pub struct GStrInteriorNulError(usize); |
339 | |
340 | impl std::error::Error for GStrInteriorNulError {} |
341 | |
342 | impl 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 | |
352 | impl 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 ] |
382 | macro_rules! gstr { |
383 | ($s:literal) => { |
384 | unsafe { $crate::GStr::from_utf8_with_nul_unchecked($crate::cstr_bytes!($s)) } |
385 | }; |
386 | } |
387 | |
388 | impl 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 | |
395 | impl PartialEq for GStr { |
396 | #[inline ] |
397 | fn eq(&self, other: &Self) -> bool { |
398 | self.as_str().eq(other.as_str()) |
399 | } |
400 | } |
401 | |
402 | impl Eq for GStr {} |
403 | |
404 | impl PartialOrd for GStr { |
405 | #[inline ] |
406 | fn partial_cmp(&self, other: &Self) -> Option<Ordering> { |
407 | Some(self.cmp(other)) |
408 | } |
409 | } |
410 | |
411 | impl Ord for GStr { |
412 | #[inline ] |
413 | fn cmp(&self, other: &Self) -> Ordering { |
414 | self.as_str().cmp(other.as_str()) |
415 | } |
416 | } |
417 | |
418 | impl 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 | |
425 | impl 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 | |
433 | impl 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 | |
440 | impl<'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(bytes:s.to_bytes_with_nul()) }) |
446 | } |
447 | } |
448 | |
449 | impl PartialEq<GStr> for String { |
450 | #[inline ] |
451 | fn eq(&self, other: &GStr) -> bool { |
452 | self.as_str() == other.as_str() |
453 | } |
454 | } |
455 | |
456 | impl PartialEq<str> for GStr { |
457 | #[inline ] |
458 | fn eq(&self, other: &str) -> bool { |
459 | self.as_str() == other |
460 | } |
461 | } |
462 | |
463 | impl PartialEq<&str> for GStr { |
464 | #[inline ] |
465 | fn eq(&self, other: &&str) -> bool { |
466 | self.as_str() == *other |
467 | } |
468 | } |
469 | |
470 | impl PartialEq<GStr> for &str { |
471 | #[inline ] |
472 | fn eq(&self, other: &GStr) -> bool { |
473 | *self == other.as_str() |
474 | } |
475 | } |
476 | |
477 | impl PartialEq<String> for GStr { |
478 | #[inline ] |
479 | fn eq(&self, other: &String) -> bool { |
480 | self.as_str() == other.as_str() |
481 | } |
482 | } |
483 | |
484 | impl PartialEq<GStr> for str { |
485 | #[inline ] |
486 | fn eq(&self, other: &GStr) -> bool { |
487 | self == other.as_str() |
488 | } |
489 | } |
490 | |
491 | impl 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 | |
498 | impl 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 | |
505 | impl 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 | |
512 | impl 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 | |
519 | impl AsRef<GStr> for GStr { |
520 | #[inline ] |
521 | fn as_ref(&self) -> &GStr { |
522 | self |
523 | } |
524 | } |
525 | |
526 | impl AsRef<str> for GStr { |
527 | #[inline ] |
528 | fn as_ref(&self) -> &str { |
529 | self.as_str() |
530 | } |
531 | } |
532 | |
533 | impl AsRef<OsStr> for GStr { |
534 | #[inline ] |
535 | fn as_ref(&self) -> &OsStr { |
536 | OsStr::new(self.as_str()) |
537 | } |
538 | } |
539 | |
540 | impl AsRef<Path> for GStr { |
541 | #[inline ] |
542 | fn as_ref(&self) -> &Path { |
543 | Path::new(self.as_str()) |
544 | } |
545 | } |
546 | |
547 | impl AsRef<[u8]> for GStr { |
548 | #[inline ] |
549 | fn as_ref(&self) -> &[u8] { |
550 | self.as_bytes() |
551 | } |
552 | } |
553 | |
554 | impl Deref for GStr { |
555 | type Target = str; |
556 | |
557 | #[inline ] |
558 | fn deref(&self) -> &str { |
559 | self.as_str() |
560 | } |
561 | } |
562 | |
563 | impl 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 | |
589 | impl GlibPtrDefault for GStr { |
590 | type GlibType = *mut c_char; |
591 | } |
592 | |
593 | impl StaticType for GStr { |
594 | #[inline ] |
595 | fn static_type() -> Type { |
596 | str::static_type() |
597 | } |
598 | } |
599 | |
600 | #[doc (hidden)] |
601 | impl 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 = 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(bytes:cstr.to_bytes_with_nul()) |
608 | } |
609 | } |
610 | |
611 | #[doc (hidden)] |
612 | impl 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)] |
620 | impl 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)] |
628 | impl 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 | |
635 | unsafe 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: *const {unknown} = gobject_ffi::g_value_get_string(value.to_glib_none().0); |
641 | let cstr: &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(bytes:cstr.to_bytes_with_nul()) |
647 | } |
648 | } |
649 | |
650 | impl 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 | |
662 | impl 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 | |
674 | impl 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: &GStr| s.as_str())) |
678 | } |
679 | } |
680 | |
681 | #[doc (hidden)] |
682 | impl<'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)] |
697 | impl<'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)] |
716 | pub struct GStringPtr(ptr::NonNull<c_char>); |
717 | |
718 | #[doc (hidden)] |
719 | unsafe impl TransparentPtrType for GStringPtr {} |
720 | |
721 | #[doc (hidden)] |
722 | impl GlibPtrDefault for GStringPtr { |
723 | type GlibType = *mut c_char; |
724 | } |
725 | |
726 | impl 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 | |
768 | impl Clone for GStringPtr { |
769 | #[inline ] |
770 | fn clone(&self) -> GStringPtr { |
771 | unsafe { GStringPtr(ptr::NonNull::new_unchecked(ptr:ffi::g_strdup(self.0.as_ptr()))) } |
772 | } |
773 | } |
774 | |
775 | impl Deref for GStringPtr { |
776 | type Target = str; |
777 | |
778 | #[inline ] |
779 | fn deref(&self) -> &str { |
780 | self.as_str() |
781 | } |
782 | } |
783 | |
784 | impl 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 | |
791 | impl 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 | |
800 | impl 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 | |
806 | impl 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 | |
813 | impl Eq for GStringPtr {} |
814 | |
815 | impl PartialEq for GStringPtr { |
816 | #[inline ] |
817 | fn eq(&self, other: &GStringPtr) -> bool { |
818 | self.partial_cmp(other) == Some(Ordering::Equal) |
819 | } |
820 | } |
821 | |
822 | impl PartialEq<GStringPtr> for String { |
823 | #[inline ] |
824 | fn eq(&self, other: &GStringPtr) -> bool { |
825 | self.partial_cmp(other) == Some(Ordering::Equal) |
826 | } |
827 | } |
828 | |
829 | impl PartialEq<GStringPtr> for GString { |
830 | #[inline ] |
831 | fn eq(&self, other: &GStringPtr) -> bool { |
832 | self.partial_cmp(other) == Some(Ordering::Equal) |
833 | } |
834 | } |
835 | |
836 | impl PartialEq<str> for GStringPtr { |
837 | #[inline ] |
838 | fn eq(&self, other: &str) -> bool { |
839 | self.partial_cmp(other) == Some(Ordering::Equal) |
840 | } |
841 | } |
842 | |
843 | impl PartialEq<&str> for GStringPtr { |
844 | #[inline ] |
845 | fn eq(&self, other: &&str) -> bool { |
846 | self.partial_cmp(other) == Some(Ordering::Equal) |
847 | } |
848 | } |
849 | |
850 | impl PartialEq<GStr> for GStringPtr { |
851 | #[inline ] |
852 | fn eq(&self, other: &GStr) -> bool { |
853 | self.partial_cmp(other) == Some(Ordering::Equal) |
854 | } |
855 | } |
856 | |
857 | impl PartialEq<&GStr> for GStringPtr { |
858 | #[inline ] |
859 | fn eq(&self, other: &&GStr) -> bool { |
860 | self.partial_cmp(other) == Some(Ordering::Equal) |
861 | } |
862 | } |
863 | |
864 | impl PartialEq<GStringPtr> for &str { |
865 | #[inline ] |
866 | fn eq(&self, other: &GStringPtr) -> bool { |
867 | self.partial_cmp(other) == Some(Ordering::Equal) |
868 | } |
869 | } |
870 | |
871 | impl PartialEq<GStringPtr> for &GStr { |
872 | #[inline ] |
873 | fn eq(&self, other: &GStringPtr) -> bool { |
874 | self.partial_cmp(other) == Some(Ordering::Equal) |
875 | } |
876 | } |
877 | |
878 | impl PartialEq<String> for GStringPtr { |
879 | #[inline ] |
880 | fn eq(&self, other: &String) -> bool { |
881 | self.partial_cmp(other) == Some(Ordering::Equal) |
882 | } |
883 | } |
884 | |
885 | impl PartialEq<GString> for GStringPtr { |
886 | #[inline ] |
887 | fn eq(&self, other: &GString) -> bool { |
888 | self.partial_cmp(other) == Some(Ordering::Equal) |
889 | } |
890 | } |
891 | |
892 | impl PartialEq<GStringPtr> for str { |
893 | #[inline ] |
894 | fn eq(&self, other: &GStringPtr) -> bool { |
895 | self.partial_cmp(other) == Some(Ordering::Equal) |
896 | } |
897 | } |
898 | |
899 | impl PartialEq<GStringPtr> for GStr { |
900 | #[inline ] |
901 | fn eq(&self, other: &GStringPtr) -> bool { |
902 | self.partial_cmp(other) == Some(Ordering::Equal) |
903 | } |
904 | } |
905 | |
906 | impl 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 | |
913 | impl Ord for GStringPtr { |
914 | #[inline ] |
915 | fn cmp(&self, other: &Self) -> std::cmp::Ordering { |
916 | unsafe { GStringPtr::strcmp(self.as_ptr(), b:other.as_ptr()) } |
917 | } |
918 | } |
919 | |
920 | impl 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 | |
927 | impl 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(), b:other.as_ptr()) }) |
931 | } |
932 | } |
933 | |
934 | impl 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 | |
941 | impl 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(), b:other.as_ptr()) }) |
945 | } |
946 | } |
947 | |
948 | impl 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 | |
955 | impl 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(), b:other.as_ptr()) }) |
959 | } |
960 | } |
961 | |
962 | impl 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 | |
969 | impl 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 | |
976 | impl 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(), b:other.as_ptr()) }) |
980 | } |
981 | } |
982 | |
983 | impl 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(), b:other.as_ptr()) }) |
987 | } |
988 | } |
989 | |
990 | impl 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 | |
997 | impl 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(), b:other.as_ptr()) }) |
1001 | } |
1002 | } |
1003 | |
1004 | impl AsRef<GStringPtr> for GStringPtr { |
1005 | #[inline ] |
1006 | fn as_ref(&self) -> &GStringPtr { |
1007 | self |
1008 | } |
1009 | } |
1010 | |
1011 | impl 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 |
1019 | const 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]<[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. |
1036 | pub struct GString(Inner); |
1037 | |
1038 | enum 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 | |
1050 | unsafe impl Send for GString {} |
1051 | unsafe impl Sync for GString {} |
1052 | |
1053 | impl 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 ] |
1349 | macro_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)] |
1358 | pub struct GStringNoTrailingNulError<T>(T); |
1359 | |
1360 | impl<T> std::error::Error for GStringNoTrailingNulError<T> where T: fmt::Debug {} |
1361 | |
1362 | impl<T> fmt::Display for GStringNoTrailingNulError<T> { |
1363 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |
1364 | fmt.write_str(data:"data provided is not nul terminated" ) |
1365 | } |
1366 | } |
1367 | |
1368 | impl<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)] |
1382 | pub struct GStringInteriorNulError<T>(T, GStrInteriorNulError); |
1383 | |
1384 | impl<T> std::error::Error for GStringInteriorNulError<T> where T: fmt::Debug {} |
1385 | |
1386 | impl<T> fmt::Display for GStringInteriorNulError<T> { |
1387 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |
1388 | fmt::Display::fmt(&self.1, f:fmt) |
1389 | } |
1390 | } |
1391 | |
1392 | impl<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)] |
1412 | pub struct GStringUtf8Error<T>(T, std::str::Utf8Error); |
1413 | |
1414 | impl<T> std::error::Error for GStringUtf8Error<T> where T: fmt::Debug {} |
1415 | |
1416 | impl<T> fmt::Display for GStringUtf8Error<T> { |
1417 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |
1418 | fmt::Display::fmt(&self.1, f:fmt) |
1419 | } |
1420 | } |
1421 | |
1422 | impl<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)] |
1441 | pub enum GStringFromError<T> { |
1442 | NoTrailingNul(GStringNoTrailingNulError<T>), |
1443 | InteriorNul(GStringInteriorNulError<T>), |
1444 | InvalidUtf8(GStringUtf8Error<T>), |
1445 | Unspecified(T), |
1446 | } |
1447 | |
1448 | impl<T> std::error::Error for GStringFromError<T> |
1449 | where |
1450 | T: fmt::Debug, |
1451 | { |
1452 | fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { |
1453 | match self { |
1454 | Self::NoTrailingNul(err: &GStringNoTrailingNulError<…>) => std::error::Error::source(self:err), |
1455 | Self::InteriorNul(err: &GStringInteriorNulError) => std::error::Error::source(self:err), |
1456 | Self::InvalidUtf8(err: &GStringUtf8Error) => std::error::Error::source(self:err), |
1457 | Self::Unspecified { .. } => None, |
1458 | } |
1459 | } |
1460 | } |
1461 | |
1462 | impl<T> fmt::Display for GStringFromError<T> { |
1463 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |
1464 | match self { |
1465 | Self::NoTrailingNul(err: &GStringNoTrailingNulError<…>) => fmt::Display::fmt(self:err, f:fmt), |
1466 | Self::InteriorNul(err: &GStringInteriorNulError) => fmt::Display::fmt(self:err, f:fmt), |
1467 | Self::InvalidUtf8(err: &GStringUtf8Error) => fmt::Display::fmt(self:err, f:fmt), |
1468 | Self::Unspecified(_) => fmt.write_str(data:"unable to convert" ), |
1469 | } |
1470 | } |
1471 | } |
1472 | |
1473 | impl<T> std::convert::From<GStringNoTrailingNulError<T>> for GStringFromError<T> { |
1474 | fn from(err: GStringNoTrailingNulError<T>) -> Self { |
1475 | GStringFromError::NoTrailingNul(err) |
1476 | } |
1477 | } |
1478 | |
1479 | impl<T> std::convert::From<GStringInteriorNulError<T>> for GStringFromError<T> { |
1480 | fn from(err: GStringInteriorNulError<T>) -> Self { |
1481 | GStringFromError::InteriorNul(err) |
1482 | } |
1483 | } |
1484 | |
1485 | impl<T> std::convert::From<GStringUtf8Error<T>> for GStringFromError<T> { |
1486 | fn from(err: GStringUtf8Error<T>) -> Self { |
1487 | GStringFromError::InvalidUtf8(err) |
1488 | } |
1489 | } |
1490 | |
1491 | impl<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 | |
1517 | impl From<std::string::FromUtf8Error> for GStringFromError<Vec<u8>> { |
1518 | #[inline ] |
1519 | fn from(e: std::string::FromUtf8Error) -> Self { |
1520 | let ue: Utf8Error = e.utf8_error(); |
1521 | Self::InvalidUtf8(GStringUtf8Error(e.into_bytes(), ue)) |
1522 | } |
1523 | } |
1524 | |
1525 | impl 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: &Box) => ffi::g_strndup(str:s.as_ptr() as *const _, n:s.len()), |
1532 | Inner::Foreign { ptr: NonNull<{unknown}>, .. } => { |
1533 | let _s: ManuallyDrop = mem::ManuallyDrop::new(self); |
1534 | ptr.as_ptr() |
1535 | } |
1536 | Inner::Inline { len: u8, ref data: &[u8; 22] } => { |
1537 | ffi::g_strndup(str:data.as_ptr() as *const _, n:len as usize) |
1538 | } |
1539 | } |
1540 | } |
1541 | } |
1542 | |
1543 | impl Default for GString { |
1544 | #[inline ] |
1545 | fn default() -> Self { |
1546 | Self::new() |
1547 | } |
1548 | } |
1549 | |
1550 | impl Clone for GString { |
1551 | #[inline ] |
1552 | fn clone(&self) -> GString { |
1553 | self.as_str().into() |
1554 | } |
1555 | } |
1556 | |
1557 | impl 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 | |
1564 | impl Drop for GString { |
1565 | #[inline ] |
1566 | fn drop(&mut self) { |
1567 | if let Inner::Foreign { ptr: NonNull<{unknown}>, .. } = self.0 { |
1568 | unsafe { |
1569 | ffi::g_free(mem:ptr.as_ptr() as *mut _); |
1570 | } |
1571 | } |
1572 | } |
1573 | } |
1574 | |
1575 | impl 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 | |
1582 | impl 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 | |
1589 | impl Borrow<GStr> for GString { |
1590 | #[inline ] |
1591 | fn borrow(&self) -> &GStr { |
1592 | self.as_gstr() |
1593 | } |
1594 | } |
1595 | |
1596 | impl Borrow<str> for GString { |
1597 | #[inline ] |
1598 | fn borrow(&self) -> &str { |
1599 | self.as_str() |
1600 | } |
1601 | } |
1602 | |
1603 | impl Ord for GString { |
1604 | #[inline ] |
1605 | fn cmp(&self, other: &GString) -> Ordering { |
1606 | self.as_str().cmp(other.as_str()) |
1607 | } |
1608 | } |
1609 | |
1610 | impl PartialOrd for GString { |
1611 | #[inline ] |
1612 | fn partial_cmp(&self, other: &GString) -> Option<Ordering> { |
1613 | Some(self.cmp(other)) |
1614 | } |
1615 | } |
1616 | |
1617 | impl PartialEq for GString { |
1618 | #[inline ] |
1619 | fn eq(&self, other: &GString) -> bool { |
1620 | self.as_str() == other.as_str() |
1621 | } |
1622 | } |
1623 | |
1624 | impl PartialEq<GString> for String { |
1625 | #[inline ] |
1626 | fn eq(&self, other: &GString) -> bool { |
1627 | self.as_str() == other.as_str() |
1628 | } |
1629 | } |
1630 | |
1631 | impl PartialEq<GStr> for GString { |
1632 | #[inline ] |
1633 | fn eq(&self, other: &GStr) -> bool { |
1634 | self.as_str() == other.as_str() |
1635 | } |
1636 | } |
1637 | |
1638 | impl PartialEq<&GStr> for GString { |
1639 | #[inline ] |
1640 | fn eq(&self, other: &&GStr) -> bool { |
1641 | self.as_str() == other.as_str() |
1642 | } |
1643 | } |
1644 | |
1645 | impl PartialEq<str> for GString { |
1646 | #[inline ] |
1647 | fn eq(&self, other: &str) -> bool { |
1648 | self.as_str() == other |
1649 | } |
1650 | } |
1651 | |
1652 | impl PartialEq<&str> for GString { |
1653 | #[inline ] |
1654 | fn eq(&self, other: &&str) -> bool { |
1655 | self.as_str() == *other |
1656 | } |
1657 | } |
1658 | |
1659 | impl PartialEq<GString> for &GStr { |
1660 | #[inline ] |
1661 | fn eq(&self, other: &GString) -> bool { |
1662 | self.as_str() == other.as_str() |
1663 | } |
1664 | } |
1665 | |
1666 | impl PartialEq<GString> for &str { |
1667 | #[inline ] |
1668 | fn eq(&self, other: &GString) -> bool { |
1669 | *self == other.as_str() |
1670 | } |
1671 | } |
1672 | |
1673 | impl PartialEq<String> for GString { |
1674 | #[inline ] |
1675 | fn eq(&self, other: &String) -> bool { |
1676 | self.as_str() == other.as_str() |
1677 | } |
1678 | } |
1679 | |
1680 | impl PartialEq<GString> for str { |
1681 | #[inline ] |
1682 | fn eq(&self, other: &GString) -> bool { |
1683 | self == other.as_str() |
1684 | } |
1685 | } |
1686 | |
1687 | impl PartialEq<GString> for GStr { |
1688 | #[inline ] |
1689 | fn eq(&self, other: &GString) -> bool { |
1690 | self.as_str() == other.as_str() |
1691 | } |
1692 | } |
1693 | |
1694 | impl 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 | |
1701 | impl 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 | |
1708 | impl 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 | |
1715 | impl 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 | |
1722 | impl PartialOrd<GString> for str { |
1723 | #[inline ] |
1724 | fn partial_cmp(&self, other: &GString) -> Option<Ordering> { |
1725 | Some(self.cmp(other)) |
1726 | } |
1727 | } |
1728 | |
1729 | impl 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 | |
1736 | impl Eq for GString {} |
1737 | |
1738 | impl AsRef<GStr> for GString { |
1739 | #[inline ] |
1740 | fn as_ref(&self) -> &GStr { |
1741 | self.as_gstr() |
1742 | } |
1743 | } |
1744 | |
1745 | impl AsRef<str> for GString { |
1746 | #[inline ] |
1747 | fn as_ref(&self) -> &str { |
1748 | self.as_str() |
1749 | } |
1750 | } |
1751 | |
1752 | impl AsRef<OsStr> for GString { |
1753 | #[inline ] |
1754 | fn as_ref(&self) -> &OsStr { |
1755 | OsStr::new(self.as_str()) |
1756 | } |
1757 | } |
1758 | |
1759 | impl AsRef<Path> for GString { |
1760 | #[inline ] |
1761 | fn as_ref(&self) -> &Path { |
1762 | Path::new(self.as_str()) |
1763 | } |
1764 | } |
1765 | |
1766 | impl AsRef<[u8]> for GString { |
1767 | #[inline ] |
1768 | fn as_ref(&self) -> &[u8] { |
1769 | self.as_str().as_bytes() |
1770 | } |
1771 | } |
1772 | |
1773 | impl Deref for GString { |
1774 | type Target = str; |
1775 | |
1776 | #[inline ] |
1777 | fn deref(&self) -> &str { |
1778 | self.as_str() |
1779 | } |
1780 | } |
1781 | |
1782 | impl From<GString> for String { |
1783 | #[inline ] |
1784 | fn from(mut s: GString) -> Self { |
1785 | match &mut s.0 { |
1786 | Inner::Native(s: &mut Box) => { |
1787 | // Moves the underlying string |
1788 | let mut s: String = String::from(mem::replace(dest:s, src:"" .into())); |
1789 | let _nul: Option = s.pop(); |
1790 | debug_assert_eq!(_nul, Some(' \0' )); |
1791 | s |
1792 | } |
1793 | Inner::Foreign { len: &mut usize, .. } if *len == 0 => String::new(), |
1794 | Inner::Foreign { ptr: &mut NonNull<{unknown}>, len: &mut usize } => unsafe { |
1795 | // Creates a copy |
1796 | let slice: &[u8] = slice::from_raw_parts(data:ptr.as_ptr() as *const u8, *len); |
1797 | std::str::from_utf8_unchecked(slice).into() |
1798 | }, |
1799 | Inner::Inline { len: &mut u8, data: &mut [u8; 22] } => unsafe { |
1800 | std::str::from_utf8_unchecked(data.get_unchecked(..*len as usize)).to_owned() |
1801 | }, |
1802 | } |
1803 | } |
1804 | } |
1805 | |
1806 | impl 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 | |
1814 | impl From<GString> for Vec<u8> { |
1815 | #[inline ] |
1816 | fn from(value: GString) -> Vec<u8> { |
1817 | value.into_bytes_with_nul() |
1818 | } |
1819 | } |
1820 | |
1821 | impl 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: usize) = memchr::memchr(needle:0, haystack:value.as_bytes()) { |
1826 | return Err(GStringInteriorNulError( |
1827 | value, |
1828 | GStrInteriorNulError(nul_pos), |
1829 | )); |
1830 | } |
1831 | let v: Vec = value.into_bytes_with_nul(); |
1832 | Ok(unsafe { CString::from_vec_with_nul_unchecked(v) }) |
1833 | } |
1834 | } |
1835 | |
1836 | impl From<GString> for OsString { |
1837 | #[inline ] |
1838 | fn from(s: GString) -> Self { |
1839 | OsString::from(String::from(s)) |
1840 | } |
1841 | } |
1842 | |
1843 | impl From<GString> for PathBuf { |
1844 | #[inline ] |
1845 | fn from(s: GString) -> Self { |
1846 | PathBuf::from(OsString::from(s)) |
1847 | } |
1848 | } |
1849 | |
1850 | impl 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(additional:1); |
1861 | s.push(ch:' \0' ); |
1862 | // No check for valid UTF-8 here |
1863 | Self(Inner::Native(s.into())) |
1864 | } |
1865 | } |
1866 | } |
1867 | |
1868 | impl 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 | |
1876 | impl<'a> From<Cow<'a, str>> for GString { |
1877 | #[inline ] |
1878 | fn from(s: Cow<'a, str>) -> Self { |
1879 | match s { |
1880 | Cow::Borrowed(s: &str) => Self::from(s), |
1881 | Cow::Owned(s: String) => Self::from(s), |
1882 | } |
1883 | } |
1884 | } |
1885 | |
1886 | impl From<&GStr> for GString { |
1887 | #[inline ] |
1888 | fn from(s: &GStr) -> GString { |
1889 | s.to_owned() |
1890 | } |
1891 | } |
1892 | |
1893 | impl 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 | |
1920 | impl From<&String> for GString { |
1921 | #[inline ] |
1922 | fn from(s: &String) -> Self { |
1923 | GString::from(s.as_str()) |
1924 | } |
1925 | } |
1926 | |
1927 | impl From<GStringPtr> for GString { |
1928 | #[inline ] |
1929 | fn from(s: GStringPtr) -> Self { |
1930 | let s: ManuallyDrop = mem::ManuallyDrop::new(s); |
1931 | let len: usize = unsafe { GStr::from_ptr(s.0.as_ptr()).len() }; |
1932 | GString(Inner::Foreign { ptr: s.0, len }) |
1933 | } |
1934 | } |
1935 | |
1936 | impl 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 = String::from_utf8(value.into_bytes_with_nul()).map_err(|e: FromUtf8Error| { |
1946 | let err: Utf8Error = 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 | |
1957 | impl 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: GStringInteriorNulError| GStringFromError::from(e).convert(func:OsString::from)) |
1963 | } |
1964 | } |
1965 | |
1966 | impl 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: GStringFromError| e.convert(func:PathBuf::from)) |
1971 | } |
1972 | } |
1973 | |
1974 | impl 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: &GStr = unsafe { GStr::from_utf8_with_nul_unchecked(bytes:value.to_bytes_with_nul()) }; |
1981 | Ok(gstr.to_owned()) |
1982 | } |
1983 | } |
1984 | |
1985 | impl<'a> From<Cow<'a, GStr>> for GString { |
1986 | #[inline ] |
1987 | fn from(s: Cow<'a, GStr>) -> Self { |
1988 | s.into_owned() |
1989 | } |
1990 | } |
1991 | |
1992 | impl<'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 | |
1999 | impl From<GString> for Cow<'_, GStr> { |
2000 | #[inline ] |
2001 | fn from(v: GString) -> Self { |
2002 | Cow::Owned(v) |
2003 | } |
2004 | } |
2005 | |
2006 | impl<'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)] |
2014 | impl 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 = 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)] |
2030 | impl 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)] |
2038 | impl 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)] |
2046 | impl 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)] |
2054 | impl 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)] |
2063 | impl 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)] |
2071 | impl 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)] |
2079 | impl 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)] |
2087 | impl 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 = 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)] |
2103 | impl 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)] |
2111 | impl 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)] |
2119 | impl 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)] |
2128 | impl<'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)] |
2144 | impl<'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)] |
2160 | impl<'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)] |
2176 | impl<'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)] |
2191 | impl 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)] |
2241 | impl 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)] |
2256 | impl 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)] |
2271 | impl 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 | |
2285 | impl GlibPtrDefault for GString { |
2286 | type GlibType = *const c_char; |
2287 | } |
2288 | |
2289 | impl StaticType for GString { |
2290 | #[inline ] |
2291 | fn static_type() -> Type { |
2292 | String::static_type() |
2293 | } |
2294 | } |
2295 | |
2296 | impl crate::value::ValueType for GString { |
2297 | type Type = String; |
2298 | } |
2299 | |
2300 | impl crate::value::ValueTypeOptional for GString {} |
2301 | |
2302 | unsafe 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 | |
2311 | impl 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 | |
2323 | impl 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: &&GString| s.as_str())) |
2327 | } |
2328 | } |
2329 | |
2330 | impl From<GString> for Value { |
2331 | #[inline ] |
2332 | fn from(s: GString) -> Self { |
2333 | unsafe { |
2334 | let mut value: Value = Value::for_value_type::<GString>(); |
2335 | gobject_ffi::g_value_take_string(value.to_glib_none_mut().0, v_string:s.into_glib_ptr()); |
2336 | value |
2337 | } |
2338 | } |
2339 | } |
2340 | |
2341 | impl StaticType for Vec<GString> { |
2342 | #[inline ] |
2343 | fn static_type() -> Type { |
2344 | <Vec<String>>::static_type() |
2345 | } |
2346 | } |
2347 | |
2348 | impl crate::value::ValueType for Vec<GString> { |
2349 | type Type = Vec<GString>; |
2350 | } |
2351 | |
2352 | unsafe 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: *const *const {unknown} = 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 | |
2362 | impl ToValue for Vec<GString> { |
2363 | #[inline ] |
2364 | fn to_value(&self) -> Value { |
2365 | unsafe { |
2366 | let mut value: 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, v_boxed: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 | |
2379 | impl From<Vec<GString>> for Value { |
2380 | #[inline ] |
2381 | fn from(mut v: Vec<GString>) -> Self { |
2382 | unsafe { |
2383 | let mut value: Value = Value::for_value_type::<Vec<GString>>(); |
2384 | let container: (*mut *mut i8, (Vec>, …)) = |
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 | v_boxed:container.0 as *const c_void, |
2389 | ); |
2390 | v.set_len(new_len:0); |
2391 | value |
2392 | } |
2393 | } |
2394 | } |
2395 | |
2396 | impl_from_glib_container_as_vec_string!(GString, *const c_char); |
2397 | impl_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. |
2401 | pub trait IntoGStr { |
2402 | fn run_with_gstr<T, F: FnOnce(&GStr) -> T>(self, f: F) -> T; |
2403 | } |
2404 | |
2405 | impl 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 | |
2412 | impl 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 | |
2419 | impl 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 |
2428 | const MAX_STACK_ALLOCATION: usize = 384; |
2429 | |
2430 | impl 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: MaybeUninit<[u8; 384]> = mem::MaybeUninit::<[u8; MAX_STACK_ALLOCATION]>::uninit(); |
2435 | let ptr: *mut u8 = s.as_mut_ptr() as *mut u8; |
2436 | let gs: &GStr = unsafe { |
2437 | ptr::copy_nonoverlapping(self.as_ptr(), dst:ptr, self.len()); |
2438 | ptr.add(self.len()).write(val:0); |
2439 | GStr::from_utf8_with_nul_unchecked(bytes:slice::from_raw_parts(data:ptr, self.len() + 1)) |
2440 | }; |
2441 | f(gs) |
2442 | } else { |
2443 | f(GString::from(self).as_gstr()) |
2444 | } |
2445 | } |
2446 | } |
2447 | |
2448 | impl IntoGStr for String { |
2449 | #[inline ] |
2450 | fn run_with_gstr<T, F: FnOnce(&GStr) -> T>(mut self, f: F) -> T { |
2451 | let len: usize = self.len(); |
2452 | if len < self.capacity() { |
2453 | self.reserve_exact(additional:1); |
2454 | self.push(ch:' \0' ); |
2455 | let gs: &GStr = 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 | |
2465 | impl 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 | |
2472 | pub const NONE_STR: Option<&'static str> = None; |
2473 | |
2474 | // rustdoc-stripper-ignore-next |
2475 | /// A trait to accept both <code>[Option]<&[str]></code> or <code>[Option]<&[GStr]></code> as |
2476 | /// an argument. |
2477 | pub trait IntoOptionalGStr { |
2478 | fn run_with_gstr<T, F: FnOnce(Option<&GStr>) -> T>(self, f: F) -> T; |
2479 | } |
2480 | |
2481 | impl<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: S) => t.run_with_gstr(|s: &GStr| f(Some(s))), |
2486 | None => f(None), |
2487 | } |
2488 | } |
2489 | } |
2490 | |
2491 | #[cfg (test)] |
2492 | #[allow (clippy::disallowed_names)] |
2493 | mod 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 | } |
2648 | |