Skip to content

Commit 04ff165

Browse files
committed
Parsing of the message structure from introspection typesupport library
This module provides a nice tree representation of the message structure.
1 parent 27342d5 commit 04ff165

File tree

2 files changed

+261
-0
lines changed

2 files changed

+261
-0
lines changed

rclrs/src/dynamic_message.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ use crate::rcl_bindings::rosidl_typesupport_introspection_c__MessageMembers_s as
1616
use crate::rcl_bindings::*;
1717

1818
mod error;
19+
mod message_structure;
1920
pub use error::*;
21+
pub use message_structure::*;
2022

2123
/// Factory for constructing messages in a certain package dynamically.
2224
///
@@ -60,6 +62,8 @@ pub struct DynamicMessageMetadata {
6062
#[allow(dead_code)]
6163
type_support_ptr: *const rosidl_message_type_support_t,
6264
#[allow(dead_code)]
65+
structure: MessageStructure,
66+
#[allow(dead_code)]
6367
fini_function: unsafe extern "C" fn(*mut std::os::raw::c_void),
6468
}
6569

@@ -172,6 +176,8 @@ impl DynamicMessagePackage {
172176
let message_members: &rosidl_message_members_t =
173177
// SAFETY: The data pointer is supposed to be always valid.
174178
unsafe { &*(type_support.data as *const rosidl_message_members_t) };
179+
// SAFETY: The message members coming from a type support library will always be valid.
180+
let structure = unsafe { MessageStructure::from_rosidl_message_members(message_members) };
175181
// The fini function will always exist.
176182
let fini_function = message_members.fini_function.unwrap();
177183
let metadata = DynamicMessageMetadata {
@@ -180,6 +186,7 @@ impl DynamicMessagePackage {
180186
&self.introspection_type_support_library,
181187
),
182188
type_support_ptr,
189+
structure,
183190
fini_function,
184191
};
185192
Ok(metadata)
Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
use std::ffi::CStr;
2+
use std::num::NonZeroUsize;
3+
4+
#[cfg(any(ros_distro = "foxy", ros_distro = "galactic"))]
5+
use crate::rcl_bindings::rosidl_typesupport_introspection_c__MessageMember as rosidl_message_member_t;
6+
#[cfg(all(not(ros_distro = "foxy"), not(ros_distro = "galactic")))]
7+
use crate::rcl_bindings::rosidl_typesupport_introspection_c__MessageMember_s as rosidl_message_member_t;
8+
#[cfg(any(ros_distro = "foxy", ros_distro = "galactic"))]
9+
use crate::rcl_bindings::rosidl_typesupport_introspection_c__MessageMembers as rosidl_message_members_t;
10+
#[cfg(all(not(ros_distro = "foxy"), not(ros_distro = "galactic")))]
11+
use crate::rcl_bindings::rosidl_typesupport_introspection_c__MessageMembers_s as rosidl_message_members_t;
12+
use crate::rcl_bindings::*;
13+
14+
/// Possible base types for fields in a message.
15+
// The field variants are self-explaining, no need to add redundant documentation.
16+
#[allow(missing_docs)]
17+
#[derive(Clone, Debug, PartialEq, Eq)]
18+
pub enum BaseType {
19+
/// AKA `float32` in ROS .msg files.
20+
Float,
21+
/// AKA `float64` in ROS .msg files.
22+
Double,
23+
LongDouble,
24+
Char,
25+
WChar,
26+
Boolean,
27+
/// AKA `byte` in ROS .msg files.
28+
Octet,
29+
/// AKA `char` in ROS .msg files
30+
Uint8,
31+
Int8,
32+
Uint16,
33+
Int16,
34+
Uint32,
35+
Int32,
36+
Uint64,
37+
Int64,
38+
String,
39+
BoundedString {
40+
upper_bound: NonZeroUsize,
41+
},
42+
WString,
43+
BoundedWString {
44+
upper_bound: NonZeroUsize,
45+
},
46+
Message(Box<MessageStructure>),
47+
}
48+
49+
/// A description of a single field in a [`DynamicMessage`][1].
50+
///
51+
/// The concrete type of a field is the combination of its [`BaseType`] with its [`ValueKind`].
52+
/// That is, the base types exist as single values, arrays, bounded sequences and unbounded sequences.
53+
///
54+
/// [1]: crate::dynamic_message::DynamicMessage
55+
#[derive(Clone, Debug, PartialEq, Eq)]
56+
pub struct MessageFieldInfo {
57+
/// The field name.
58+
pub name: String,
59+
/// The base type – number, string, etc.
60+
pub base_type: BaseType,
61+
/// Whether the field is a simple value, an array, or a (bounded) sequence.
62+
pub value_kind: ValueKind,
63+
pub(crate) string_upper_bound: usize,
64+
pub(crate) resize_function:
65+
Option<unsafe extern "C" fn(arg1: *mut std::os::raw::c_void, size: usize) -> bool>,
66+
pub(crate) offset: usize,
67+
}
68+
69+
/// A description of the structure of a message.
70+
///
71+
/// Namely, the list of fields and their types.
72+
#[derive(Clone, Debug, PartialEq, Eq)]
73+
pub struct MessageStructure {
74+
/// The set of fields in the message, ordered by their offset in the message.
75+
///
76+
/// A `Vec` is easier to handle and faster than a `HashMap` for typical numbers of message fields.
77+
/// If you need a `HashMap`, simply create your own from this `Vec`.
78+
pub fields: Vec<MessageFieldInfo>,
79+
/// The size of this structure in bytes.
80+
pub size: usize,
81+
/// The namespace of this type. This is something like `geometry_msgs__msg`.
82+
pub namespace: String,
83+
/// The name of this type. This does not contain the package name.
84+
pub type_name: String,
85+
}
86+
87+
/// Information on whether a field is a single value or a list of some kind.
88+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
89+
pub enum ValueKind {
90+
/// This field is a single value, which includes string values.
91+
Simple,
92+
/// This field is an array of values.
93+
Array {
94+
/// The array length.
95+
length: usize,
96+
},
97+
/// This field is a [`Sequence`][1] of values.
98+
///
99+
/// [1]: rosidl_runtime_rs::Sequence
100+
Sequence,
101+
/// This field is a [`BoundedSequence`][1] of values.
102+
///
103+
/// [1]: rosidl_runtime_rs::BoundedSequence
104+
BoundedSequence {
105+
/// The maximum sequence length.
106+
upper_bound: usize,
107+
},
108+
}
109+
110+
// ========================= impl for BaseType =========================
111+
112+
impl BaseType {
113+
// The inner message type support will be nullptr except for the case of a nested message.
114+
// That function must be unsafe, since it is possible to safely create a garbage non-null
115+
// pointer.
116+
unsafe fn new(
117+
type_id: u8,
118+
string_upper_bound: Option<NonZeroUsize>,
119+
inner: *const rosidl_message_type_support_t,
120+
) -> Self {
121+
#[cfg(all(not(ros_distro = "foxy"), not(ros_distro = "galactic")))]
122+
use rosidl_typesupport_introspection_c_field_types::*;
123+
match u32::from(type_id) {
124+
x if x == rosidl_typesupport_introspection_c__ROS_TYPE_FLOAT as u32 => Self::Float,
125+
x if x == rosidl_typesupport_introspection_c__ROS_TYPE_DOUBLE as u32 => Self::Double,
126+
x if x == rosidl_typesupport_introspection_c__ROS_TYPE_LONG_DOUBLE as u32 => {
127+
Self::LongDouble
128+
}
129+
x if x == rosidl_typesupport_introspection_c__ROS_TYPE_CHAR as u32 => Self::Char,
130+
x if x == rosidl_typesupport_introspection_c__ROS_TYPE_WCHAR as u32 => Self::WChar,
131+
x if x == rosidl_typesupport_introspection_c__ROS_TYPE_BOOLEAN as u32 => Self::Boolean,
132+
x if x == rosidl_typesupport_introspection_c__ROS_TYPE_OCTET as u32 => Self::Octet,
133+
x if x == rosidl_typesupport_introspection_c__ROS_TYPE_UINT8 as u32 => Self::Uint8,
134+
x if x == rosidl_typesupport_introspection_c__ROS_TYPE_INT8 as u32 => Self::Int8,
135+
x if x == rosidl_typesupport_introspection_c__ROS_TYPE_UINT16 as u32 => Self::Uint16,
136+
x if x == rosidl_typesupport_introspection_c__ROS_TYPE_INT16 as u32 => Self::Int16,
137+
x if x == rosidl_typesupport_introspection_c__ROS_TYPE_UINT32 as u32 => Self::Uint32,
138+
x if x == rosidl_typesupport_introspection_c__ROS_TYPE_INT32 as u32 => Self::Int32,
139+
x if x == rosidl_typesupport_introspection_c__ROS_TYPE_UINT64 as u32 => Self::Uint64,
140+
x if x == rosidl_typesupport_introspection_c__ROS_TYPE_INT64 as u32 => Self::Int64,
141+
x if x == rosidl_typesupport_introspection_c__ROS_TYPE_STRING as u32 => {
142+
match string_upper_bound {
143+
None => Self::String,
144+
Some(upper_bound) => Self::BoundedString { upper_bound },
145+
}
146+
}
147+
x if x == rosidl_typesupport_introspection_c__ROS_TYPE_WSTRING as u32 => {
148+
match string_upper_bound {
149+
None => Self::WString,
150+
Some(upper_bound) => Self::BoundedWString { upper_bound },
151+
}
152+
}
153+
x if x == rosidl_typesupport_introspection_c__ROS_TYPE_MESSAGE as u32 => {
154+
assert!(!inner.is_null());
155+
let type_support: &rosidl_message_type_support_t = &*inner;
156+
let message_members: &rosidl_message_members_t =
157+
// SAFETY: The data pointer is supposed to be always valid.
158+
&*(type_support.data as *const rosidl_message_members_t);
159+
let structure = MessageStructure::from_rosidl_message_members(message_members);
160+
Self::Message(Box::new(structure))
161+
}
162+
_ => panic!("Invalid field type"),
163+
}
164+
}
165+
}
166+
167+
// ========================= impl for MessageFieldInfo =========================
168+
169+
impl MessageFieldInfo {
170+
// That function must be unsafe, since it is possible to safely create a garbage non-null
171+
// pointer and store it in a rosidl_message_member_t.
172+
unsafe fn from(rosidl_message_member: &rosidl_message_member_t) -> Self {
173+
debug_assert!(!rosidl_message_member.name_.is_null());
174+
let name = /*unsafe*/ { CStr::from_ptr(rosidl_message_member.name_) }
175+
.to_string_lossy()
176+
.into_owned();
177+
let value_kind = match (
178+
rosidl_message_member.is_array_,
179+
rosidl_message_member.resize_function.is_some(),
180+
rosidl_message_member.is_upper_bound_,
181+
) {
182+
(false, _, _) => ValueKind::Simple,
183+
(true, false, _) => ValueKind::Array {
184+
length: rosidl_message_member.array_size_,
185+
},
186+
(true, true, false) => {
187+
assert_eq!(rosidl_message_member.array_size_, 0);
188+
ValueKind::Sequence
189+
}
190+
(true, true, true) => ValueKind::BoundedSequence {
191+
upper_bound: rosidl_message_member.array_size_,
192+
},
193+
};
194+
Self {
195+
name,
196+
base_type: BaseType::new(
197+
rosidl_message_member.type_id_,
198+
NonZeroUsize::new(rosidl_message_member.string_upper_bound_),
199+
rosidl_message_member.members_,
200+
),
201+
value_kind,
202+
string_upper_bound: rosidl_message_member.string_upper_bound_,
203+
resize_function: rosidl_message_member.resize_function,
204+
offset: usize::try_from(rosidl_message_member.offset_).unwrap(),
205+
}
206+
}
207+
}
208+
209+
// ========================= impl for MessageStructure =========================
210+
211+
impl MessageStructure {
212+
/// Parses the C struct containing a list of fields.
213+
// That function must be unsafe, since it is possible to safely create a garbage non-null
214+
// pointer and store it in a rosidl_message_members_t.
215+
pub(crate) unsafe fn from_rosidl_message_members(
216+
message_members: &rosidl_message_members_t,
217+
) -> Self {
218+
debug_assert!(!message_members.members_.is_null());
219+
let num_fields: usize = usize::try_from(message_members.member_count_).unwrap();
220+
let mut fields: Vec<_> = (0..num_fields)
221+
.map(|i| {
222+
// SAFETY: This is an array as per the documentation
223+
let rosidl_message_member: &rosidl_message_member_t =
224+
/*unsafe*/ { &*message_members.members_.add(i) };
225+
// SAFETY: This is a valid string pointer
226+
MessageFieldInfo::from(rosidl_message_member)
227+
})
228+
.collect();
229+
fields.sort_by_key(|field_info| field_info.offset);
230+
// SAFETY: Immediate conversion into owned string.
231+
let namespace = /*unsafe*/ {
232+
CStr::from_ptr(message_members.message_namespace_)
233+
.to_string_lossy()
234+
.into_owned()
235+
};
236+
// SAFETY: Immediate conversion into owned string.
237+
let type_name = /*unsafe*/ {
238+
CStr::from_ptr(message_members.message_name_)
239+
.to_string_lossy()
240+
.into_owned()
241+
};
242+
Self {
243+
fields,
244+
size: message_members.size_of_,
245+
namespace,
246+
type_name,
247+
}
248+
}
249+
250+
/// Gets the field info corresponding to the specified field name, if any.
251+
pub fn get_field_info(&self, field_name: &str) -> Option<&MessageFieldInfo> {
252+
self.fields.iter().find(|field| field.name == field_name)
253+
}
254+
}

0 commit comments

Comments
 (0)