|
| 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