use std::{
borrow::Borrow,
cmp::Ordering,
ffi::CStr,
fmt::{self, Debug, Display},
hash::{Hash, Hasher},
ops::{Deref, DerefMut},
};
#[cfg(feature = "serde")]
mod serde;
use crate::{sequence::Sequence, traits::SequenceAlloc};
#[repr(C)]
pub struct String {
data: *mut std::os::raw::c_char,
size: usize,
capacity: usize,
}
#[repr(C)]
pub struct WString {
data: *mut u16,
size: usize,
capacity: usize,
}
#[derive(Clone, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
#[repr(transparent)]
pub struct BoundedString<const N: usize> {
inner: String,
}
#[derive(Clone, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
#[repr(transparent)]
pub struct BoundedWString<const N: usize> {
inner: WString,
}
#[derive(Debug)]
pub struct StringExceedsBoundsError {
pub len: usize,
pub upper_bound: usize,
}
macro_rules! string_impl {
($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) => {
#[link(name = "rosidl_runtime_c")]
unsafe extern "C" {
fn $init(s: *mut $string) -> bool;
fn $fini(s: *mut $string);
fn $assignn(s: *mut $string, value: *const $char_type, n: usize) -> bool;
fn $sequence_init(seq: *mut Sequence<$string>, size: usize) -> bool;
fn $sequence_fini(seq: *mut Sequence<$string>);
fn $sequence_copy(
in_seq: *const Sequence<$string>,
out_seq: *mut Sequence<$string>,
) -> bool;
}
impl Clone for $string {
fn clone(&self) -> Self {
let mut msg = Self::default();
if !unsafe { $assignn(&mut msg as *mut _, self.data as *const _, self.size) } {
panic!("$assignn failed");
}
msg
}
}
impl Debug for $string {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
Debug::fmt(&self.to_string(), f)
}
}
impl Default for $string {
fn default() -> Self {
let mut msg = Self {
data: std::ptr::null_mut(),
size: 0,
capacity: 0,
};
if !unsafe { $init(&mut msg as *mut _) } {
panic!("$init failed");
}
msg
}
}
impl Deref for $string {
type Target = [$char_type];
fn deref(&self) -> &Self::Target {
unsafe { std::slice::from_raw_parts(self.data, self.size) }
}
}
impl DerefMut for $string {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { std::slice::from_raw_parts_mut(self.data, self.size) }
}
}
impl Display for $string {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
let u8_slice = unsafe {
std::slice::from_raw_parts(self.data as *mut $unsigned_char_type, self.size)
};
let converted = std::string::String::$string_conversion_func(u8_slice);
Display::fmt(&converted, f)
}
}
impl Drop for $string {
fn drop(&mut self) {
unsafe {
$fini(self as *mut _);
}
}
}
impl Eq for $string {}
impl Extend<char> for $string {
fn extend<I: IntoIterator<Item = char>>(&mut self, iter: I) {
let mut s = self.to_string();
s.extend(iter);
*self = Self::from(s.as_str());
}
}
impl<'a> Extend<&'a char> for $string {
fn extend<I: IntoIterator<Item = &'a char>>(&mut self, iter: I) {
self.extend(iter.into_iter().cloned());
}
}
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 Hash for $string {
fn hash<H: Hasher>(&self, state: &mut H) {
self.deref().hash(state)
}
}
impl Ord for $string {
fn cmp(&self, other: &Self) -> Ordering {
self.deref().cmp(other.deref())
}
}
impl PartialEq for $string {
fn eq(&self, other: &Self) -> bool {
self.deref().eq(other.deref())
}
}
impl PartialOrd for $string {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
unsafe impl Send for $string {}
unsafe impl Sync for $string {}
impl SequenceAlloc for $string {
fn sequence_init(seq: &mut Sequence<Self>, size: usize) -> bool {
unsafe { $sequence_init(seq as *mut _, size) }
}
fn sequence_fini(seq: &mut Sequence<Self>) {
unsafe { $sequence_fini(seq as *mut _) }
}
fn sequence_copy(in_seq: &Sequence<Self>, out_seq: &mut Sequence<Self>) -> bool {
unsafe { $sequence_copy(in_seq as *const _, out_seq as *mut _) }
}
}
};
}
string_impl!(
String,
std::os::raw::c_char,
u8,
from_utf8_lossy,
rosidl_runtime_c__String__init,
rosidl_runtime_c__String__fini,
rosidl_runtime_c__String__assignn,
rosidl_runtime_c__String__Sequence__init,
rosidl_runtime_c__String__Sequence__fini,
rosidl_runtime_c__String__Sequence__copy
);
string_impl!(
WString,
u16,
u16,
from_utf16_lossy,
rosidl_runtime_c__U16String__init,
rosidl_runtime_c__U16String__fini,
rosidl_runtime_c__U16String__assignn,
rosidl_runtime_c__U16String__Sequence__init,
rosidl_runtime_c__U16String__Sequence__fini,
rosidl_runtime_c__U16String__Sequence__copy
);
impl<T> From<T> for String
where
T: Borrow<str>,
{
fn from(s: T) -> Self {
let mut msg = Self {
data: std::ptr::null_mut(),
size: 0,
capacity: 0,
};
let s = s.borrow();
if !unsafe {
rosidl_runtime_c__String__assignn(&mut msg as *mut _, s.as_ptr() as *const _, s.len())
} {
panic!("rosidl_runtime_c__String__assignn failed");
}
msg
}
}
impl String {
pub fn to_cstr(&self) -> &CStr {
unsafe { CStr::from_ptr(self.data as *const _) }
}
}
impl From<&str> for WString {
fn from(s: &str) -> Self {
let mut msg = Self {
data: std::ptr::null_mut(),
size: 0,
capacity: 0,
};
let buf: Vec<u16> = s.encode_utf16().collect();
if !unsafe {
rosidl_runtime_c__U16String__assignn(
&mut msg as *mut _,
buf.as_ptr() as *const _,
buf.len(),
)
} {
panic!("rosidl_runtime_c__U16String__assignn failed");
}
msg
}
}
impl<const N: usize> Debug for BoundedString<N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
Debug::fmt(&self.inner, f)
}
}
impl<const N: usize> Deref for BoundedString<N> {
type Target = [std::os::raw::c_char];
fn deref(&self) -> &Self::Target {
self.inner.deref()
}
}
impl<const N: usize> DerefMut for BoundedString<N> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.inner.deref_mut()
}
}
impl<const N: usize> Display for BoundedString<N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
Display::fmt(&self.inner, f)
}
}
impl<const N: usize> SequenceAlloc for BoundedString<N> {
fn sequence_init(seq: &mut Sequence<Self>, size: usize) -> bool {
unsafe {
rosidl_runtime_c__String__Sequence__init(seq as *mut Sequence<Self> as *mut _, size)
}
}
fn sequence_fini(seq: &mut Sequence<Self>) {
unsafe { rosidl_runtime_c__String__Sequence__fini(seq as *mut Sequence<Self> as *mut _) }
}
fn sequence_copy(in_seq: &Sequence<Self>, out_seq: &mut Sequence<Self>) -> bool {
unsafe {
<String as SequenceAlloc>::sequence_copy(
std::mem::transmute::<&Sequence<Self>, &Sequence<String>>(in_seq),
std::mem::transmute::<&mut Sequence<Self>, &mut Sequence<String>>(out_seq),
)
}
}
}
impl<const N: usize> TryFrom<&str> for BoundedString<N> {
type Error = StringExceedsBoundsError;
fn try_from(s: &str) -> Result<Self, Self::Error> {
let length = s.len();
if length <= N {
Ok(Self {
inner: String::from(s),
})
} else {
Err(StringExceedsBoundsError {
len: length,
upper_bound: N,
})
}
}
}
impl<const N: usize> Debug for BoundedWString<N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
Debug::fmt(&self.inner, f)
}
}
impl<const N: usize> Deref for BoundedWString<N> {
type Target = [u16];
fn deref(&self) -> &Self::Target {
self.inner.deref()
}
}
impl<const N: usize> DerefMut for BoundedWString<N> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.inner.deref_mut()
}
}
impl<const N: usize> Display for BoundedWString<N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
Display::fmt(&self.inner, f)
}
}
impl<const N: usize> SequenceAlloc for BoundedWString<N> {
fn sequence_init(seq: &mut Sequence<Self>, size: usize) -> bool {
unsafe {
rosidl_runtime_c__U16String__Sequence__init(seq as *mut Sequence<Self> as *mut _, size)
}
}
fn sequence_fini(seq: &mut Sequence<Self>) {
unsafe { rosidl_runtime_c__U16String__Sequence__fini(seq as *mut Sequence<Self> as *mut _) }
}
fn sequence_copy(in_seq: &Sequence<Self>, out_seq: &mut Sequence<Self>) -> bool {
unsafe {
<WString as SequenceAlloc>::sequence_copy(
std::mem::transmute::<&Sequence<Self>, &Sequence<WString>>(in_seq),
std::mem::transmute::<&mut Sequence<Self>, &mut Sequence<WString>>(out_seq),
)
}
}
}
impl<const N: usize> TryFrom<&str> for BoundedWString<N> {
type Error = StringExceedsBoundsError;
fn try_from(s: &str) -> Result<Self, Self::Error> {
let inner = WString::from(s);
if inner.size <= N {
Ok(Self { inner })
} else {
Err(StringExceedsBoundsError {
len: inner.size,
upper_bound: N,
})
}
}
}
impl Display for StringExceedsBoundsError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(
f,
"BoundedString with upper bound {} initialized with len {}",
self.upper_bound, self.len
)
}
}
impl std::error::Error for StringExceedsBoundsError {}
#[cfg(test)]
mod tests {
use quickcheck::{Arbitrary, Gen};
use super::*;
impl Arbitrary for String {
fn arbitrary(g: &mut Gen) -> Self {
std::string::String::arbitrary(g).as_str().into()
}
}
impl Arbitrary for WString {
fn arbitrary(g: &mut Gen) -> Self {
std::string::String::arbitrary(g).as_str().into()
}
}
fn generate_ascii(g: &mut Gen) -> char {
let c = u8::min(u8::arbitrary(g), 127);
c as char
}
impl Arbitrary for BoundedString<256> {
fn arbitrary(g: &mut Gen) -> Self {
let len = u8::arbitrary(g);
let s: std::string::String = (0..len).map(|_| generate_ascii(g)).collect();
s.as_str().try_into().unwrap()
}
}
impl Arbitrary for BoundedWString<256> {
fn arbitrary(g: &mut Gen) -> Self {
let len = u8::arbitrary(g);
let s: std::string::String = (0..len).map(|_| generate_ascii(g)).collect();
s.as_str().try_into().unwrap()
}
}
#[test]
fn string_from_char_iterator() {
let expected = String::from("abc");
let actual = "abc".chars().collect::<String>();
assert_eq!(expected, actual);
let expected = String::from("");
let actual = "".chars().collect::<String>();
assert_eq!(expected, actual);
let expected = String::from("Grüß Gott! 𝕊");
let actual = "Grüß Gott! 𝕊".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() {
let expected = WString::from("abc");
let actual = "abc".chars().collect::<WString>();
assert_eq!(expected, actual);
let expected = WString::from("");
let actual = "".chars().collect::<WString>();
assert_eq!(expected, actual);
let expected = WString::from("Grüß Gott! 𝕊");
let actual = "Grüß Gott! 𝕊".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);
}
}