do_not_use_testing_rosidl_runtime_rs/
string.rs

1use std::cmp::Ordering;
2use std::ffi::CStr;
3use std::fmt::{self, Debug, Display};
4use std::hash::{Hash, Hasher};
5use std::ops::{Deref, DerefMut};
6
7#[cfg(feature = "serde")]
8mod serde;
9
10use crate::sequence::Sequence;
11use crate::traits::SequenceAlloc;
12
13/// A zero-terminated string of 8-bit characters.
14///
15/// The layout of this type is the same as `rosidl_runtime_c__String`. See the
16/// [`Message`](crate::Message) trait for background information on this topic.
17///
18///
19/// # Example
20///
21/// ```
22/// # use rosidl_runtime_rs::String;
23/// let mut s = String::from("Grüß Gott!");
24/// // Conversion back to a std::string::String is done with the ToString trait from the standard
25/// // library.
26/// assert_eq!(&s.to_string(), "Grüß Gott!");
27/// ```
28#[repr(C)]
29pub struct String {
30    /// Dynamic memory in this type is allocated and deallocated by C, but this is a detail that is managed by
31    /// the relevant functions and trait impls.
32    data: *mut std::os::raw::c_char,
33    size: usize,
34    capacity: usize,
35}
36
37/// A zero-terminated string of 16-bit characters.
38///
39/// The layout of this type is the same as `rosidl_runtime_c__U16String`. See the
40/// [`Message`](crate::Message) trait for background information on this topic.
41///
42/// # Example
43///
44/// ```
45/// # use rosidl_runtime_rs::WString;
46/// let mut s = WString::from("Grüß Gott!");
47/// // Conversion back to a std::string::String is done with the ToString trait from the standard
48/// // library.
49/// assert_eq!(&s.to_string(), "Grüß Gott!");
50/// ```
51#[repr(C)]
52pub struct WString {
53    data: *mut std::os::raw::c_ushort,
54    size: usize,
55    capacity: usize,
56}
57
58/// A zero-terminated string of 8-bit characters with a length limit.
59///
60/// The same as [`String`], but it cannot be constructed from a string that is too large.
61/// The length is measured as the number of Unicode scalar values, not bytes.
62///
63/// # Example
64///
65/// ```
66/// # use rosidl_runtime_rs::BoundedString;
67/// let mut maybe_str = BoundedString::<3>::try_from("noo!");
68/// assert!(maybe_str.is_err());
69/// maybe_str = BoundedString::<3>::try_from("ok!");
70/// assert!(maybe_str.is_ok());
71/// let bounded_str = maybe_str.unwrap();
72/// assert_eq!(&bounded_str.to_string(), "ok!");
73/// ```
74#[derive(Clone, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
75#[repr(transparent)]
76pub struct BoundedString<const N: usize> {
77    inner: String,
78}
79
80/// A zero-terminated string of 16-bit characters with a length limit.
81///
82/// The same as [`WString`], but it cannot be constructed from a string that is too large.
83/// The length is measured as the number of Unicode scalar values, not bytes.
84///
85/// # Example
86///
87/// ```
88/// # use rosidl_runtime_rs::BoundedWString;
89/// let mut maybe_wstr = BoundedWString::<3>::try_from("noo!");
90/// assert!(maybe_wstr.is_err());
91/// maybe_wstr = BoundedWString::<3>::try_from("ok!");
92/// assert!(maybe_wstr.is_ok());
93/// let bounded_wstr = maybe_wstr.unwrap();
94/// assert_eq!(&bounded_wstr.to_string(), "ok!");
95/// ```
96#[derive(Clone, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
97#[repr(transparent)]
98pub struct BoundedWString<const N: usize> {
99    inner: WString,
100}
101
102/// Error type for [`BoundedString::try_from()`] and [`BoundedWString::try_from()`].
103#[derive(Debug)]
104pub struct StringExceedsBoundsError {
105    /// The actual length the string would have after the operation.
106    pub len: usize,
107    /// The upper bound on the string length.
108    pub upper_bound: usize,
109}
110
111// ========================= impls for String and WString =========================
112
113// There is a lot of redundancy between String and WString, which this macro aims to reduce.
114macro_rules! string_impl {
115    ($string:ty, $char_type:ty, $unsigned_char_type:ty, $string_conversion_func:ident, $init:ident, $fini:ident, $assignn:ident, $sequence_init:ident, $sequence_fini:ident, $sequence_copy:ident) => {
116        #[link(name = "rosidl_runtime_c")]
117        extern "C" {
118            fn $init(s: *mut $string) -> bool;
119            fn $fini(s: *mut $string);
120            fn $assignn(s: *mut $string, value: *const $char_type, n: usize) -> bool;
121            fn $sequence_init(seq: *mut Sequence<$string>, size: usize) -> bool;
122            fn $sequence_fini(seq: *mut Sequence<$string>);
123            fn $sequence_copy(
124                in_seq: *const Sequence<$string>,
125                out_seq: *mut Sequence<$string>,
126            ) -> bool;
127        }
128
129        impl Clone for $string {
130            fn clone(&self) -> Self {
131                let mut msg = Self::default();
132                // SAFETY: This is doing the same thing as rosidl_runtime_c__String__copy.
133                if !unsafe { $assignn(&mut msg as *mut _, self.data as *const _, self.size) } {
134                    panic!("$assignn failed");
135                }
136                msg
137            }
138        }
139
140        impl Debug for $string {
141            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
142                Debug::fmt(&self.to_string(), f)
143            }
144        }
145
146        impl Default for $string {
147            fn default() -> Self {
148                let mut msg = Self {
149                    data: std::ptr::null_mut(),
150                    size: 0,
151                    capacity: 0,
152                };
153                // SAFETY: Passing in a zeroed string is safe.
154                if !unsafe { $init(&mut msg as *mut _) } {
155                    panic!("$init failed");
156                }
157                msg
158            }
159        }
160
161        // It's not guaranteed that there are no interior null bytes, hence no Deref to CStr.
162        // This does not include the null byte at the end!
163        impl Deref for $string {
164            type Target = [$char_type];
165            fn deref(&self) -> &Self::Target {
166                // SAFETY: self.data points to self.size consecutive, initialized elements and
167                // isn't modified externally.
168                unsafe { std::slice::from_raw_parts(self.data, self.size) }
169            }
170        }
171
172        impl DerefMut for $string {
173            fn deref_mut(&mut self) -> &mut Self::Target {
174                // SAFETY: self.data points to self.size consecutive, initialized elements and
175                // isn't modified externally.
176                unsafe { std::slice::from_raw_parts_mut(self.data, self.size) }
177            }
178        }
179
180        impl Display for $string {
181            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
182                // SAFETY: Same as deref, but with an additional cast to the unsigned type.
183                // See also https://users.rust-lang.org/t/how-to-convert-i8-to-u8/16308/11
184                let u8_slice = unsafe {
185                    std::slice::from_raw_parts(self.data as *mut $unsigned_char_type, self.size)
186                };
187                let converted = std::string::String::$string_conversion_func(u8_slice);
188                Display::fmt(&converted, f)
189            }
190        }
191
192        impl Drop for $string {
193            fn drop(&mut self) {
194                // SAFETY: There are no special preconditions to the fini function.
195                unsafe {
196                    $fini(self as *mut _);
197                }
198            }
199        }
200
201        impl Eq for $string {}
202
203        impl Extend<char> for $string {
204            fn extend<I: IntoIterator<Item = char>>(&mut self, iter: I) {
205                let mut s = self.to_string();
206                s.extend(iter);
207                *self = Self::from(s.as_str());
208            }
209        }
210
211        impl<'a> Extend<&'a char> for $string {
212            fn extend<I: IntoIterator<Item = &'a char>>(&mut self, iter: I) {
213                self.extend(iter.into_iter().cloned());
214            }
215        }
216
217        impl FromIterator<char> for $string {
218            fn from_iter<I: IntoIterator<Item = char>>(iter: I) -> Self {
219                let mut buf = <$string>::default();
220                buf.extend(iter);
221                buf
222            }
223        }
224
225        impl<'a> FromIterator<&'a char> for $string {
226            fn from_iter<I: IntoIterator<Item = &'a char>>(iter: I) -> Self {
227                let mut buf = <$string>::default();
228                buf.extend(iter);
229                buf
230            }
231        }
232
233        impl Hash for $string {
234            fn hash<H: Hasher>(&self, state: &mut H) {
235                self.deref().hash(state)
236            }
237        }
238
239        impl Ord for $string {
240            fn cmp(&self, other: &Self) -> Ordering {
241                self.deref().cmp(other.deref())
242            }
243        }
244
245        impl PartialEq for $string {
246            fn eq(&self, other: &Self) -> bool {
247                self.deref().eq(other.deref())
248            }
249        }
250
251        impl PartialOrd for $string {
252            fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
253                self.deref().partial_cmp(other.deref())
254            }
255        }
256
257        // SAFETY: A string is a simple data structure, and therefore not thread-specific.
258        unsafe impl Send for $string {}
259        // SAFETY: A string does not have interior mutability, so it can be shared.
260        unsafe impl Sync for $string {}
261
262        impl SequenceAlloc for $string {
263            fn sequence_init(seq: &mut Sequence<Self>, size: usize) -> bool {
264                // SAFETY: There are no special preconditions to the sequence_init function.
265                unsafe { $sequence_init(seq as *mut _, size) }
266            }
267            fn sequence_fini(seq: &mut Sequence<Self>) {
268                // SAFETY: There are no special preconditions to the sequence_fini function.
269                unsafe { $sequence_fini(seq as *mut _) }
270            }
271            fn sequence_copy(in_seq: &Sequence<Self>, out_seq: &mut Sequence<Self>) -> bool {
272                // SAFETY: There are no special preconditions to the sequence_copy function.
273                unsafe { $sequence_copy(in_seq as *const _, out_seq as *mut _) }
274            }
275        }
276    };
277}
278
279string_impl!(
280    String,
281    std::os::raw::c_char,
282    u8,
283    from_utf8_lossy,
284    rosidl_runtime_c__String__init,
285    rosidl_runtime_c__String__fini,
286    rosidl_runtime_c__String__assignn,
287    rosidl_runtime_c__String__Sequence__init,
288    rosidl_runtime_c__String__Sequence__fini,
289    rosidl_runtime_c__String__Sequence__copy
290);
291string_impl!(
292    WString,
293    std::os::raw::c_ushort,
294    u16,
295    from_utf16_lossy,
296    rosidl_runtime_c__U16String__init,
297    rosidl_runtime_c__U16String__fini,
298    rosidl_runtime_c__U16String__assignn,
299    rosidl_runtime_c__U16String__Sequence__init,
300    rosidl_runtime_c__U16String__Sequence__fini,
301    rosidl_runtime_c__U16String__Sequence__copy
302);
303
304impl From<&str> for String {
305    fn from(s: &str) -> Self {
306        let mut msg = Self {
307            data: std::ptr::null_mut(),
308            size: 0,
309            capacity: 0,
310        };
311        // SAFETY: It's okay to pass a non-zero-terminated string here since assignn uses the
312        // specified length and will append the 0 byte to the dest string itself.
313        if !unsafe {
314            rosidl_runtime_c__String__assignn(&mut msg as *mut _, s.as_ptr() as *const _, s.len())
315        } {
316            panic!("rosidl_runtime_c__String__assignn failed");
317        }
318        msg
319    }
320}
321
322impl String {
323    /// Creates a CStr from this String.
324    ///
325    /// This scales with the length of the string but does not create copy of the string.
326    /// See also [`CStr::from_ptr()`].
327    pub fn to_cstr(&self) -> &CStr {
328        // SAFETY: self.data is a valid pointer and won't change.
329        // Also, the lifetime of the CStr is the same as self, which is correct.
330        unsafe { CStr::from_ptr(self.data as *const _) }
331    }
332}
333
334impl From<&str> for WString {
335    fn from(s: &str) -> Self {
336        let mut msg = Self {
337            data: std::ptr::null_mut(),
338            size: 0,
339            capacity: 0,
340        };
341        let buf: Vec<u16> = s.encode_utf16().collect();
342        // SAFETY: It's okay to pass a non-zero-terminated string here since assignn uses the
343        // specified length and will append the 0 to the dest string itself.
344        if !unsafe {
345            rosidl_runtime_c__U16String__assignn(
346                &mut msg as *mut _,
347                buf.as_ptr() as *const _,
348                buf.len(),
349            )
350        } {
351            panic!("rosidl_runtime_c__U16String__assignn failed");
352        }
353        msg
354    }
355}
356
357// ========================= impl for BoundedString =========================
358
359impl<const N: usize> Debug for BoundedString<N> {
360    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
361        Debug::fmt(&self.inner, f)
362    }
363}
364
365impl<const N: usize> Deref for BoundedString<N> {
366    type Target = [std::os::raw::c_char];
367    fn deref(&self) -> &Self::Target {
368        self.inner.deref()
369    }
370}
371
372impl<const N: usize> DerefMut for BoundedString<N> {
373    fn deref_mut(&mut self) -> &mut Self::Target {
374        self.inner.deref_mut()
375    }
376}
377
378impl<const N: usize> Display for BoundedString<N> {
379    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
380        Display::fmt(&self.inner, f)
381    }
382}
383
384impl<const N: usize> SequenceAlloc for BoundedString<N> {
385    fn sequence_init(seq: &mut Sequence<Self>, size: usize) -> bool {
386        // SAFETY: There are no special preconditions to the rosidl_runtime_c__String__Sequence__init function.
387        unsafe {
388            rosidl_runtime_c__String__Sequence__init(seq as *mut Sequence<Self> as *mut _, size)
389        }
390    }
391    fn sequence_fini(seq: &mut Sequence<Self>) {
392        // SAFETY: There are no special preconditions to the rosidl_runtime_c__String__Sequence__fini function.
393        unsafe { rosidl_runtime_c__String__Sequence__fini(seq as *mut Sequence<Self> as *mut _) }
394    }
395    fn sequence_copy(in_seq: &Sequence<Self>, out_seq: &mut Sequence<Self>) -> bool {
396        // SAFETY: Transmute of a transparent type to the inner type is fine
397        unsafe {
398            <String as SequenceAlloc>::sequence_copy(
399                std::mem::transmute::<&Sequence<Self>, &Sequence<String>>(in_seq),
400                std::mem::transmute::<&mut Sequence<Self>, &mut Sequence<String>>(out_seq),
401            )
402        }
403    }
404}
405
406impl<const N: usize> TryFrom<&str> for BoundedString<N> {
407    type Error = StringExceedsBoundsError;
408    fn try_from(s: &str) -> Result<Self, Self::Error> {
409        let length = s.chars().count();
410        if length <= N {
411            Ok(Self {
412                inner: String::from(s),
413            })
414        } else {
415            Err(StringExceedsBoundsError {
416                len: length,
417                upper_bound: N,
418            })
419        }
420    }
421}
422
423// ========================= impl for BoundedWString =========================
424
425impl<const N: usize> Debug for BoundedWString<N> {
426    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
427        Debug::fmt(&self.inner, f)
428    }
429}
430
431impl<const N: usize> Deref for BoundedWString<N> {
432    type Target = [u16];
433    fn deref(&self) -> &Self::Target {
434        self.inner.deref()
435    }
436}
437
438impl<const N: usize> DerefMut for BoundedWString<N> {
439    fn deref_mut(&mut self) -> &mut Self::Target {
440        self.inner.deref_mut()
441    }
442}
443
444impl<const N: usize> Display for BoundedWString<N> {
445    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
446        Display::fmt(&self.inner, f)
447    }
448}
449
450impl<const N: usize> SequenceAlloc for BoundedWString<N> {
451    fn sequence_init(seq: &mut Sequence<Self>, size: usize) -> bool {
452        // SAFETY: There are no special preconditions to the rosidl_runtime_c__U16String__Sequence__init function.
453        unsafe {
454            rosidl_runtime_c__U16String__Sequence__init(seq as *mut Sequence<Self> as *mut _, size)
455        }
456    }
457    fn sequence_fini(seq: &mut Sequence<Self>) {
458        // SAFETY: There are no special preconditions to the rosidl_runtime_c__U16String__Sequence__fini function.
459        unsafe { rosidl_runtime_c__U16String__Sequence__fini(seq as *mut Sequence<Self> as *mut _) }
460    }
461    fn sequence_copy(in_seq: &Sequence<Self>, out_seq: &mut Sequence<Self>) -> bool {
462        // SAFETY: Transmute of a transparent type to the inner type is fine
463        unsafe {
464            <WString as SequenceAlloc>::sequence_copy(
465                std::mem::transmute::<&Sequence<Self>, &Sequence<WString>>(in_seq),
466                std::mem::transmute::<&mut Sequence<Self>, &mut Sequence<WString>>(out_seq),
467            )
468        }
469    }
470}
471
472impl<const N: usize> TryFrom<&str> for BoundedWString<N> {
473    type Error = StringExceedsBoundsError;
474    fn try_from(s: &str) -> Result<Self, Self::Error> {
475        let length = s.chars().count();
476        if length <= N {
477            Ok(Self {
478                inner: WString::from(s),
479            })
480        } else {
481            Err(StringExceedsBoundsError {
482                len: length,
483                upper_bound: N,
484            })
485        }
486    }
487}
488
489// ========================= impl for StringExceedsBoundsError =========================
490
491impl Display for StringExceedsBoundsError {
492    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
493        write!(
494            f,
495            "BoundedString with upper bound {} initialized with len {}",
496            self.upper_bound, self.len
497        )
498    }
499}
500
501impl std::error::Error for StringExceedsBoundsError {}
502
503#[cfg(test)]
504mod tests {
505    use quickcheck::{Arbitrary, Gen};
506
507    use super::*;
508
509    impl Arbitrary for String {
510        fn arbitrary(g: &mut Gen) -> Self {
511            std::string::String::arbitrary(g).as_str().into()
512        }
513    }
514
515    impl Arbitrary for WString {
516        fn arbitrary(g: &mut Gen) -> Self {
517            std::string::String::arbitrary(g).as_str().into()
518        }
519    }
520
521    impl Arbitrary for BoundedString<256> {
522        fn arbitrary(g: &mut Gen) -> Self {
523            let len = u8::arbitrary(g);
524            let s: std::string::String = (0..len).map(|_| char::arbitrary(g)).collect();
525            s.as_str().try_into().unwrap()
526        }
527    }
528
529    impl Arbitrary for BoundedWString<256> {
530        fn arbitrary(g: &mut Gen) -> Self {
531            let len = u8::arbitrary(g);
532            let s: std::string::String = (0..len).map(|_| char::arbitrary(g)).collect();
533            s.as_str().try_into().unwrap()
534        }
535    }
536
537    #[test]
538    fn string_from_char_iterator() {
539        // Base char case
540        let expected = String::from("abc");
541        let actual = "abc".chars().collect::<String>();
542
543        assert_eq!(expected, actual);
544
545        // Empty case
546        let expected = String::from("");
547        let actual = "".chars().collect::<String>();
548
549        assert_eq!(expected, actual);
550
551        // Non-ascii char case
552        let expected = String::from("Grüß Gott! 𝕊");
553        let actual = "Grüß Gott! 𝕊".chars().collect::<String>();
554
555        assert_eq!(expected, actual);
556    }
557
558    #[test]
559    fn extend_string_with_char_iterator() {
560        let expected = WString::from("abcdef");
561        let mut actual = WString::from("abc");
562        actual.extend("def".chars());
563
564        assert_eq!(expected, actual);
565    }
566
567    #[test]
568    fn wstring_from_char_iterator() {
569        // Base char case
570        let expected = WString::from("abc");
571        let actual = "abc".chars().collect::<WString>();
572
573        assert_eq!(expected, actual);
574
575        // Empty case
576        let expected = WString::from("");
577        let actual = "".chars().collect::<WString>();
578
579        assert_eq!(expected, actual);
580
581        // Non-ascii char case
582        let expected = WString::from("Grüß Gott! 𝕊");
583        let actual = "Grüß Gott! 𝕊".chars().collect::<WString>();
584
585        assert_eq!(expected, actual);
586    }
587
588    #[test]
589    fn extend_wstring_with_char_iterator() {
590        let expected = WString::from("abcdef");
591        let mut actual = WString::from("abc");
592        actual.extend("def".chars());
593
594        assert_eq!(expected, actual);
595    }
596}