gstreamer/id_str/
compat.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3// rustdoc-stripper-ignore-next
4//! The `IdStr` compatibility implementation.
5//!
6//! See the higher level module documentation for details.
7
8use glib::{GStr, GString, IntoGStr};
9use std::{
10    cmp,
11    ffi::CStr,
12    fmt,
13    hash::{Hash, Hasher},
14    ops::Deref,
15};
16
17use kstring::KString;
18
19// rustdoc-stripper-ignore-next
20/// An UTF-8 immutable string type with optimizations for short values (len < 16).
21#[derive(Clone, Debug)]
22#[doc(alias = "GstIdStr")]
23pub struct IdStr(KString);
24
25impl IdStr {
26    // In order to keep the same API and usability as `id_str_bindings::IdStr` regarding
27    // the ability to efficiently deref to `&GStr`, the internal `KString` is always built
28    // from a string with a nul terminator.
29
30    #[doc(alias = "gst_id_str_new")]
31    #[inline]
32    pub const fn new() -> IdStr {
33        skip_assert_initialized!();
34        // Always include the nul terminator in the internal string
35        IdStr(KString::from_static("\0"))
36    }
37
38    // rustdoc-stripper-ignore-next
39    /// Builds an `IdStr` from the given static `GStr`.
40    ///
41    /// This constructor performs optimizations which other constructors can't rely on.
42    ///
43    /// To build an `IdStr` from a string literal, use the [`idstr`](crate::idstr) macro.
44    #[inline]
45    pub fn from_static<T: AsRef<GStr> + ?Sized>(value: &'static T) -> IdStr {
46        skip_assert_initialized!();
47        let gstr = value.as_ref();
48        unsafe {
49            let str_with_nul = std::str::from_utf8_unchecked(std::slice::from_raw_parts(
50                gstr.as_ptr() as *const _,
51                gstr.as_bytes_with_nul().len(),
52            ));
53
54            IdStr(KString::from_static(str_with_nul))
55        }
56    }
57
58    #[doc(alias = "gst_id_str_new")]
59    #[inline]
60    pub fn from(value: impl AsRef<str>) -> IdStr {
61        skip_assert_initialized!();
62        let mut id = IdStr::new();
63        id.set(value);
64
65        id
66    }
67
68    #[doc(alias = "gst_id_str_get_len")]
69    #[inline]
70    pub fn len(&self) -> usize {
71        // The internal string ends with a nul terminator
72        self.0.len() - 1
73    }
74
75    #[inline]
76    pub fn is_empty(&self) -> bool {
77        // The internal string ends with a nul terminator
78        self.0.len() == 1
79    }
80
81    #[inline]
82    pub fn as_bytes(&self) -> &[u8] {
83        // The internal string ends with a nul terminator
84        &self.0.as_bytes()[..IdStr::len(self)]
85    }
86
87    #[inline]
88    fn as_bytes_with_nul(&self) -> &[u8] {
89        // The internal string ends with a nul terminator
90        self.0.as_bytes()
91    }
92
93    #[inline]
94    pub fn as_str(&self) -> &str {
95        unsafe {
96            // Safety: the internal value is guaranteed to be an utf-8 string.
97            std::str::from_utf8_unchecked(self.as_bytes())
98        }
99    }
100
101    #[doc(alias = "gst_id_str_as_str")]
102    #[inline]
103    pub fn as_gstr(&self) -> &GStr {
104        unsafe {
105            // Safety: the internal value is guaranteed to be an utf-8 string.
106            GStr::from_utf8_with_nul_unchecked(self.as_bytes_with_nul())
107        }
108    }
109
110    #[doc(alias = "gst_id_str_as_str")]
111    #[inline]
112    pub fn as_cstr(&self) -> &CStr {
113        unsafe {
114            // Safety: the internal value is guaranteed to be an utf-8 string
115            // thus to not contain any nul bytes except for the terminator.
116            CStr::from_bytes_with_nul_unchecked(self.as_bytes_with_nul())
117        }
118    }
119
120    // rustdoc-stripper-ignore-next
121    /// Sets `self` to the static string `value`.
122    ///
123    /// This function performs optimizations which [IdStr::set] can't rely on.
124    ///
125    /// To build an `IdStr` from a string literal, use the [`idstr`](crate::idstr) macro.
126    #[doc(alias = "gst_id_str_set_static_str")]
127    #[doc(alias = "gst_id_str_set_static_str_with_len")]
128    #[inline]
129    pub fn set_static<T: AsRef<GStr> + ?Sized>(&mut self, value: &'static T) {
130        unsafe {
131            let gstr = value.as_ref();
132            // Safety: the `GStr` value is guaranteed to be an utf-8 string
133            // ending with a nul terminator.
134            let str_with_nul = std::str::from_utf8_unchecked(std::slice::from_raw_parts(
135                gstr.as_ptr() as *const _,
136                gstr.as_bytes_with_nul().len(),
137            ));
138
139            self.0 = KString::from_static(str_with_nul);
140        }
141    }
142
143    // rustdoc-stripper-ignore-next
144    /// Sets `self` to the string `value`.
145    ///
146    /// For a static value, use [IdStr::set_static] which can perform optimizations.
147    ///
148    /// To build an `IdStr` from a string literal, use the [`idstr`](crate::idstr) macro.
149    #[doc(alias = "gst_id_str_set")]
150    #[doc(alias = "gst_id_str_set_with_len")]
151    #[inline]
152    pub fn set(&mut self, value: impl AsRef<str>) {
153        self.0 = value.as_ref().run_with_gstr(|gstr| unsafe {
154            let str_with_nul = std::str::from_utf8_unchecked(std::slice::from_raw_parts(
155                gstr.as_ptr() as *const _,
156                gstr.as_bytes_with_nul().len(),
157            ));
158
159            KString::from_ref(str_with_nul)
160        });
161    }
162}
163
164impl Default for IdStr {
165    fn default() -> Self {
166        Self::new()
167    }
168}
169
170impl Deref for IdStr {
171    type Target = GStr;
172
173    fn deref(&self) -> &Self::Target {
174        self.as_gstr()
175    }
176}
177
178impl AsRef<IdStr> for IdStr {
179    #[inline]
180    fn as_ref(&self) -> &IdStr {
181        self
182    }
183}
184
185impl AsRef<str> for IdStr {
186    #[inline]
187    fn as_ref(&self) -> &str {
188        self.as_str()
189    }
190}
191
192impl AsRef<GStr> for IdStr {
193    #[inline]
194    fn as_ref(&self) -> &GStr {
195        self.as_gstr()
196    }
197}
198
199impl AsRef<CStr> for IdStr {
200    #[inline]
201    fn as_ref(&self) -> &CStr {
202        self.as_cstr()
203    }
204}
205
206impl From<&str> for IdStr {
207    #[inline]
208    fn from(value: &str) -> IdStr {
209        skip_assert_initialized!();
210        value.run_with_gstr(|gstr| unsafe {
211            // Safety: the `GStr` value is guaranteed to be an utf-8 string
212            // ending with a nul terminator.
213            let str_with_nul = std::str::from_utf8_unchecked(std::slice::from_raw_parts(
214                gstr.as_ptr() as *const _,
215                gstr.as_bytes_with_nul().len(),
216            ));
217
218            IdStr(KString::from_ref(str_with_nul))
219        })
220    }
221}
222
223impl From<&String> for IdStr {
224    #[inline]
225    fn from(value: &String) -> IdStr {
226        skip_assert_initialized!();
227        value.run_with_gstr(|gstr| unsafe {
228            // Safety: the `GStr` value is guaranteed to be an utf-8 string
229            // ending with a nul terminator.
230            let str_with_nul = std::str::from_utf8_unchecked(std::slice::from_raw_parts(
231                gstr.as_ptr() as *const _,
232                gstr.as_bytes_with_nul().len(),
233            ));
234
235            IdStr(KString::from_ref(str_with_nul))
236        })
237    }
238}
239
240impl From<String> for IdStr {
241    #[inline]
242    fn from(value: String) -> IdStr {
243        skip_assert_initialized!();
244        value.run_with_gstr(|gstr| unsafe {
245            // Safety: the `GStr` value is guaranteed to be an utf-8 string
246            // ending with a nul terminator.
247            let str_with_nul = std::str::from_utf8_unchecked(std::slice::from_raw_parts(
248                gstr.as_ptr() as *const _,
249                gstr.as_bytes_with_nul().len(),
250            ));
251
252            IdStr(KString::from_ref(str_with_nul))
253        })
254    }
255}
256
257impl From<&GStr> for IdStr {
258    #[inline]
259    fn from(value: &GStr) -> IdStr {
260        skip_assert_initialized!();
261        unsafe {
262            // Safety: the `GStr` value is guaranteed to be an utf-8 string
263            // ending with a nul terminator.
264            let str_with_nul = std::str::from_utf8_unchecked(std::slice::from_raw_parts(
265                value.as_ptr() as *const _,
266                value.as_bytes_with_nul().len(),
267            ));
268
269            IdStr(KString::from_ref(str_with_nul))
270        }
271    }
272}
273
274impl From<&GString> for IdStr {
275    #[inline]
276    fn from(value: &GString) -> IdStr {
277        skip_assert_initialized!();
278        unsafe {
279            // Safety: the `GString` value is guaranteed to be an utf-8 string
280            // ending with a nul terminator.
281            let str_with_nul = std::str::from_utf8_unchecked(std::slice::from_raw_parts(
282                value.as_ptr() as *const _,
283                value.len() + 1,
284            ));
285
286            IdStr(KString::from_ref(str_with_nul))
287        }
288    }
289}
290
291impl From<GString> for IdStr {
292    #[inline]
293    fn from(value: GString) -> IdStr {
294        skip_assert_initialized!();
295        unsafe {
296            // Safety: the `GString` value is guaranteed to be an utf-8 string
297            // ending with a nul terminator.
298            let str_with_nul = std::str::from_utf8_unchecked(std::slice::from_raw_parts(
299                value.as_ptr() as *const _,
300                value.len() + 1,
301            ));
302
303            IdStr(KString::from_ref(str_with_nul))
304        }
305    }
306}
307
308impl fmt::Display for IdStr {
309    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
310        f.write_str(self.as_gstr())
311    }
312}
313
314impl PartialOrd for IdStr {
315    #[inline]
316    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
317        Some(self.cmp(other))
318    }
319}
320
321impl PartialEq for IdStr {
322    #[inline]
323    fn eq(&self, other: &Self) -> bool {
324        self.0 == other.0
325    }
326}
327
328impl PartialOrd<&IdStr> for IdStr {
329    #[inline]
330    fn partial_cmp(&self, other: &&IdStr) -> Option<cmp::Ordering> {
331        Some(self.0.cmp(&other.0))
332    }
333}
334
335impl PartialEq<&IdStr> for IdStr {
336    #[inline]
337    fn eq(&self, other: &&IdStr) -> bool {
338        self.0 == other.0
339    }
340}
341
342impl PartialOrd<IdStr> for &IdStr {
343    #[inline]
344    fn partial_cmp(&self, other: &IdStr) -> Option<cmp::Ordering> {
345        Some(self.0.cmp(&other.0))
346    }
347}
348
349impl PartialEq<IdStr> for &IdStr {
350    #[inline]
351    fn eq(&self, other: &IdStr) -> bool {
352        self.0 == other.0
353    }
354}
355
356impl Ord for IdStr {
357    #[inline]
358    fn cmp(&self, other: &Self) -> cmp::Ordering {
359        self.0.cmp(&other.0)
360    }
361}
362
363impl Eq for IdStr {}
364
365impl PartialOrd<&GStr> for IdStr {
366    #[inline]
367    fn partial_cmp(&self, other: &&GStr) -> Option<cmp::Ordering> {
368        self.as_gstr().partial_cmp(*other)
369    }
370}
371
372impl PartialEq<&GStr> for IdStr {
373    #[inline]
374    fn eq(&self, other: &&GStr) -> bool {
375        self.as_gstr() == *other
376    }
377}
378
379impl PartialOrd<GStr> for IdStr {
380    #[inline]
381    fn partial_cmp(&self, other: &GStr) -> Option<cmp::Ordering> {
382        self.as_gstr().partial_cmp(other)
383    }
384}
385
386impl PartialEq<GStr> for IdStr {
387    #[inline]
388    fn eq(&self, other: &GStr) -> bool {
389        self.as_gstr() == other
390    }
391}
392
393impl PartialOrd<IdStr> for &GStr {
394    #[inline]
395    fn partial_cmp(&self, other: &IdStr) -> Option<cmp::Ordering> {
396        (*self).partial_cmp(other.as_gstr())
397    }
398}
399
400impl PartialEq<IdStr> for &GStr {
401    #[inline]
402    fn eq(&self, other: &IdStr) -> bool {
403        (*self) == other.as_gstr()
404    }
405}
406
407impl PartialOrd<IdStr> for GStr {
408    #[inline]
409    fn partial_cmp(&self, other: &IdStr) -> Option<cmp::Ordering> {
410        self.partial_cmp(other.as_gstr())
411    }
412}
413
414impl PartialEq<IdStr> for GStr {
415    #[inline]
416    fn eq(&self, other: &IdStr) -> bool {
417        self == other.as_gstr()
418    }
419}
420
421impl PartialOrd<&str> for IdStr {
422    #[inline]
423    fn partial_cmp(&self, other: &&str) -> Option<cmp::Ordering> {
424        self.as_gstr().partial_cmp(*other)
425    }
426}
427
428impl PartialEq<&str> for IdStr {
429    #[inline]
430    fn eq(&self, other: &&str) -> bool {
431        self.as_gstr() == *other
432    }
433}
434
435impl PartialOrd<str> for IdStr {
436    #[inline]
437    fn partial_cmp(&self, other: &str) -> Option<cmp::Ordering> {
438        self.as_gstr().partial_cmp(other)
439    }
440}
441
442impl PartialEq<str> for IdStr {
443    #[inline]
444    fn eq(&self, other: &str) -> bool {
445        self.as_gstr() == other
446    }
447}
448
449impl PartialOrd<IdStr> for &str {
450    #[inline]
451    fn partial_cmp(&self, other: &IdStr) -> Option<cmp::Ordering> {
452        (*self).partial_cmp(other.as_gstr())
453    }
454}
455
456impl PartialEq<IdStr> for &str {
457    #[inline]
458    fn eq(&self, other: &IdStr) -> bool {
459        (*self) == other.as_gstr()
460    }
461}
462
463impl PartialOrd<IdStr> for str {
464    #[inline]
465    fn partial_cmp(&self, other: &IdStr) -> Option<cmp::Ordering> {
466        self.partial_cmp(other.as_gstr())
467    }
468}
469
470impl PartialEq<IdStr> for str {
471    #[inline]
472    fn eq(&self, other: &IdStr) -> bool {
473        self == other.as_gstr()
474    }
475}
476
477impl PartialOrd<GString> for IdStr {
478    #[inline]
479    fn partial_cmp(&self, other: &GString) -> Option<cmp::Ordering> {
480        self.as_gstr().partial_cmp(other)
481    }
482}
483
484impl PartialEq<GString> for IdStr {
485    #[inline]
486    fn eq(&self, other: &GString) -> bool {
487        self.as_gstr() == other
488    }
489}
490
491impl PartialOrd<IdStr> for GString {
492    #[inline]
493    fn partial_cmp(&self, other: &IdStr) -> Option<cmp::Ordering> {
494        self.partial_cmp(other.as_gstr())
495    }
496}
497
498impl PartialEq<IdStr> for GString {
499    #[inline]
500    fn eq(&self, other: &IdStr) -> bool {
501        self == other.as_gstr()
502    }
503}
504
505impl PartialOrd<String> for IdStr {
506    #[inline]
507    fn partial_cmp(&self, other: &String) -> Option<cmp::Ordering> {
508        self.as_gstr().partial_cmp(other)
509    }
510}
511
512impl PartialEq<String> for IdStr {
513    #[inline]
514    fn eq(&self, other: &String) -> bool {
515        self.as_gstr() == other
516    }
517}
518
519impl PartialOrd<IdStr> for String {
520    #[inline]
521    fn partial_cmp(&self, other: &IdStr) -> Option<cmp::Ordering> {
522        self.partial_cmp(other.as_gstr())
523    }
524}
525
526impl PartialEq<IdStr> for String {
527    #[inline]
528    fn eq(&self, other: &IdStr) -> bool {
529        self == other.as_gstr()
530    }
531}
532
533impl Hash for IdStr {
534    fn hash<H: Hasher>(&self, state: &mut H) {
535        self.as_gstr().hash(state)
536    }
537}
538
539unsafe impl Send for IdStr {}
540unsafe impl Sync for IdStr {}
541
542// Tests are mutualised between this implementation and the one in id_str_bindings
543// See gstreamer/id_str/mod.rs