Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend string types #293

Merged
merged 7 commits into from
Nov 13, 2022
Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 106 additions & 5 deletions rosidl_runtime_rs/src/string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ use std::ffi::CStr;
use std::fmt::{self, Debug, Display};
use std::hash::{Hash, Hasher};
use std::ops::{Deref, DerefMut};
use std::ptr;
maspe36 marked this conversation as resolved.
Show resolved Hide resolved
use std::ptr::slice_from_raw_parts;

#[cfg(feature = "serde")]
mod serde;
Expand Down Expand Up @@ -135,7 +137,7 @@ macro_rules! string_impl {
};
// SAFETY: Passing in a zeroed string is safe.
if !unsafe { $init(&mut msg as *mut _) } {
panic!("Sinit failed");
panic!("$init failed");
maspe36 marked this conversation as resolved.
Show resolved Hide resolved
}
msg
}
Expand Down Expand Up @@ -224,10 +226,47 @@ macro_rules! string_impl {
}
}

impl FromIterator<char> for $string {
fn from_iter<I: IntoIterator<Item = char>>(iter: I) -> Self {
let mut buf = <$string>::default();
buf.extend(iter);
buf
}
}

impl<'a> FromIterator<&'a char> for $string {
fn from_iter<I: IntoIterator<Item = &'a char>>(iter: I) -> Self {
let mut buf = <$string>::default();
buf.extend(iter);
buf
}
}

impl Extend<char> for $string {
maspe36 marked this conversation as resolved.
Show resolved Hide resolved
fn extend<I: IntoIterator<Item = char>>(&mut self, iter: I) {
let mut v = self.to_vec();
let iterator = iter.into_iter();

iterator.for_each(|c| {
v.push(c as $char_type);
});

// SAFETY: It's okay to pass a non-zero-terminated string here since assignn
// uses the specified length and will append the 0 to the dest string itself.
if !unsafe { $assignn(self as *mut _, v.as_ptr() as *const _, v.len()) } {
panic!("$assignn failed");
}
}
maspe36 marked this conversation as resolved.
Show resolved Hide resolved
}

impl<'a> Extend<&'a char> for $string {
fn extend<I: IntoIterator<Item = &'a char>>(&mut self, iter: I) {
self.extend(iter.into_iter().cloned());
}
}

// SAFETY: A string is a simple data structure, and therefore not thread-specific.
unsafe impl Send for $string {}
// SAFETY: A string does not have interior mutability, so it can be shared.
unsafe impl Sync for $string {}

impl SequenceAlloc for $string {
fn sequence_init(seq: &mut Sequence<Self>, size: usize) -> bool {
Expand All @@ -243,6 +282,20 @@ macro_rules! string_impl {
unsafe { $sequence_copy(in_seq as *const _, out_seq as *mut _) }
}
}

impl $string {
/// Returns a copy of `self` as a vector without the trailing null byte.
pub fn to_vec(&self) -> Vec<$char_type> {
let mut v: Vec<$char_type> = vec![];

// SAFETY: self.data points to self.size consecutive, initialized elements and
// isn't modified externally.
let s = unsafe { slice_from_raw_parts(self.data, self.size).as_ref() };
v.extend_from_slice(s.unwrap());

v
}
}
};
}

Expand Down Expand Up @@ -274,7 +327,7 @@ string_impl!(
impl From<&str> for String {
fn from(s: &str) -> Self {
let mut msg = Self {
data: std::ptr::null_mut(),
data: ptr::null_mut(),
size: 0,
capacity: 0,
};
Expand Down Expand Up @@ -304,7 +357,7 @@ impl String {
impl From<&str> for WString {
fn from(s: &str) -> Self {
let mut msg = Self {
data: std::ptr::null_mut(),
data: ptr::null_mut(),
size: 0,
capacity: 0,
};
Expand Down Expand Up @@ -503,4 +556,52 @@ mod tests {
s.as_str().try_into().unwrap()
}
}

#[test]
fn string_from_char_iterator() {
// Base char case
let expected = String::from("abc");
let actual = "abc".chars().collect::<String>();

assert_eq!(expected, actual);

// Empty case
let expected = String::from("");
let actual = "".chars().collect::<String>();

assert_eq!(expected, actual);
}

#[test]
fn extend_string_with_char_iterator() {
let expected = WString::from("abcdef");
let mut actual = WString::from("abc");
actual.extend("def".chars());

assert_eq!(expected, actual);
}

#[test]
fn wstring_from_char_iterator() {
// Base char case
let expected = WString::from("abc");
let actual = "abc".chars().collect::<WString>();

assert_eq!(expected, actual);

// Empty case
let expected = WString::from("");
let actual = "".chars().collect::<WString>();

assert_eq!(expected, actual);
}

#[test]
fn extend_wstring_with_char_iterator() {
let expected = WString::from("abcdef");
let mut actual = WString::from("abc");
actual.extend("def".chars());

assert_eq!(expected, actual);
}
}