1- use std:: { ffi:: CStr , num:: NonZeroUsize } ;
1+ use std:: { ffi:: CStr , mem , num:: NonZeroUsize , os :: raw :: c_char , os :: raw :: c_void } ;
22
33use super :: TypeErasedSequence ;
44use crate :: rcl_bindings:: {
@@ -61,6 +61,44 @@ pub struct MessageFieldInfo {
6161 pub ( crate ) offset : usize ,
6262}
6363
64+ type ResizeFunction = Option < unsafe extern "C" fn ( arg1 : * mut c_void , size : usize ) -> bool > ;
65+
66+ #[ repr( C ) ]
67+ #[ derive( Clone , Copy ) ]
68+ struct MessageMemberPrefix {
69+ name_ : * const c_char ,
70+ type_id_ : u8 ,
71+ string_upper_bound_ : usize ,
72+ members_ : * const rosidl_message_type_support_t ,
73+ is_key_ : u8 ,
74+ is_array_ : u8 ,
75+ array_size_ : usize ,
76+ is_upper_bound_ : u8 ,
77+ offset_ : u32 ,
78+ default_value_ : * const c_void ,
79+ size_function : * const c_void ,
80+ get_const_function : * const c_void ,
81+ get_function : * const c_void ,
82+ fetch_function : * const c_void ,
83+ assign_function : * const c_void ,
84+ resize_function : * const c_void ,
85+ }
86+
87+ impl MessageMemberPrefix {
88+ fn is_valid_for_message ( & self , message_size : usize ) -> bool {
89+ use rosidl_typesupport_introspection_c_field_types:: * ;
90+
91+ !self . name_ . is_null ( )
92+ && ( rosidl_typesupport_introspection_c__ROS_TYPE_FLOAT as u8
93+ ..=rosidl_typesupport_introspection_c__ROS_TYPE_MESSAGE as u8 )
94+ . contains ( & self . type_id_ )
95+ && [ self . is_key_ , self . is_array_ , self . is_upper_bound_ ]
96+ . iter ( )
97+ . all ( |value| * value <= 1 )
98+ && usize:: try_from ( self . offset_ ) . is_ok_and ( |offset| offset < message_size)
99+ }
100+ }
101+
64102/// A description of the structure of a message.
65103///
66104/// Namely, the list of fields and their types.
@@ -196,38 +234,75 @@ impl MessageFieldInfo {
196234 // That function must be unsafe, since it is possible to safely create a garbage non-null
197235 // pointer and store it in a rosidl_message_member_t.
198236 unsafe fn from ( rosidl_message_member : & rosidl_message_member_t ) -> Self {
199- debug_assert ! ( !rosidl_message_member . name_ . is_null ( ) ) ;
200- let name = /*unsafe*/ { CStr :: from_ptr ( rosidl_message_member. name_ ) }
201- . to_string_lossy ( )
202- . into_owned ( ) ;
203- let value_kind = match (
237+ Self :: from_parts (
238+ rosidl_message_member. name_ ,
239+ rosidl_message_member . type_id_ ,
240+ rosidl_message_member . string_upper_bound_ ,
241+ rosidl_message_member . members_ ,
204242 rosidl_message_member. is_array_ ,
205- rosidl_message_member. resize_function . is_some ( ) ,
243+ rosidl_message_member. array_size_ ,
206244 rosidl_message_member. is_upper_bound_ ,
207- ) {
245+ rosidl_message_member. resize_function ,
246+ rosidl_message_member. offset_ ,
247+ )
248+ }
249+
250+ unsafe fn from_prefix ( rosidl_message_member : & MessageMemberPrefix ) -> Self {
251+ let resize_function: ResizeFunction = if rosidl_message_member. resize_function . is_null ( ) {
252+ None
253+ } else {
254+ Some ( mem:: transmute :: <
255+ * const c_void ,
256+ unsafe extern "C" fn ( * mut c_void , usize ) -> bool ,
257+ > ( rosidl_message_member. resize_function ) )
258+ } ;
259+
260+ Self :: from_parts (
261+ rosidl_message_member. name_ ,
262+ rosidl_message_member. type_id_ ,
263+ rosidl_message_member. string_upper_bound_ ,
264+ rosidl_message_member. members_ ,
265+ rosidl_message_member. is_array_ != 0 ,
266+ rosidl_message_member. array_size_ ,
267+ rosidl_message_member. is_upper_bound_ != 0 ,
268+ resize_function,
269+ rosidl_message_member. offset_ ,
270+ )
271+ }
272+
273+ unsafe fn from_parts (
274+ name : * const c_char ,
275+ type_id : u8 ,
276+ string_upper_bound : usize ,
277+ members : * const rosidl_message_type_support_t ,
278+ is_array : bool ,
279+ array_size : usize ,
280+ is_upper_bound : bool ,
281+ resize_function : ResizeFunction ,
282+ offset : u32 ,
283+ ) -> Self {
284+ debug_assert ! ( !name. is_null( ) ) ;
285+ let name = /*unsafe*/ { CStr :: from_ptr ( name) }
286+ . to_string_lossy ( )
287+ . into_owned ( ) ;
288+ let value_kind = match ( is_array, resize_function. is_some ( ) , is_upper_bound) {
208289 ( false , _, _) => ValueKind :: Simple ,
209- ( true , false , _) => ValueKind :: Array {
210- length : rosidl_message_member. array_size_ ,
211- } ,
290+ ( true , false , _) => ValueKind :: Array { length : array_size } ,
212291 ( true , true , false ) => {
213- assert_eq ! ( rosidl_message_member . array_size_ , 0 ) ;
292+ assert_eq ! ( array_size , 0 ) ;
214293 ValueKind :: Sequence
215294 }
216295 ( true , true , true ) => ValueKind :: BoundedSequence {
217- upper_bound : rosidl_message_member . array_size_ ,
296+ upper_bound : array_size ,
218297 } ,
219298 } ;
220299 Self {
221300 name,
222- base_type : BaseType :: new (
223- rosidl_message_member. type_id_ ,
224- NonZeroUsize :: new ( rosidl_message_member. string_upper_bound_ ) ,
225- rosidl_message_member. members_ ,
226- ) ,
301+ base_type : BaseType :: new ( type_id, NonZeroUsize :: new ( string_upper_bound) , members) ,
227302 value_kind,
228- string_upper_bound : rosidl_message_member . string_upper_bound_ ,
229- resize_function : rosidl_message_member . resize_function ,
230- offset : usize:: try_from ( rosidl_message_member . offset_ ) . unwrap ( ) ,
303+ string_upper_bound,
304+ resize_function,
305+ offset : usize:: try_from ( offset ) . unwrap ( ) ,
231306 }
232307 }
233308}
@@ -251,6 +326,61 @@ impl MessageFieldInfo {
251326// ========================= impl for MessageStructure =========================
252327
253328impl MessageStructure {
329+ unsafe fn message_member_stride ( message_members : & rosidl_message_members_t ) -> usize {
330+ let current_stride = mem:: size_of :: < rosidl_message_member_t > ( ) ;
331+ let prefix_stride = mem:: size_of :: < MessageMemberPrefix > ( ) ;
332+ if message_members. member_count_ < 2 {
333+ return current_stride;
334+ }
335+
336+ let base = message_members. members_ as * const u8 ;
337+ for stride in [ current_stride, prefix_stride] {
338+ let candidate = & * ( base. add ( stride) as * const MessageMemberPrefix ) ;
339+ if candidate. is_valid_for_message ( message_members. size_of_ ) {
340+ return stride;
341+ }
342+ }
343+
344+ current_stride
345+ }
346+
347+ cfg_if:: cfg_if! {
348+ if #[ cfg( any( ros_distro = "humble" , ros_distro = "jazzy" , ros_distro = "kilted" ) ) ] {
349+ unsafe fn message_fields_from_rosidl_message_members(
350+ message_members: & rosidl_message_members_t,
351+ num_fields: usize ,
352+ ) -> Vec <MessageFieldInfo > {
353+ ( 0 ..num_fields)
354+ . map( |i| {
355+ // SAFETY: This is an array as per the documentation
356+ let rosidl_message_member: & rosidl_message_member_t =
357+ /*unsafe*/ { & * message_members. members_. add( i) } ;
358+ // SAFETY: This is a valid string pointer
359+ MessageFieldInfo :: from( rosidl_message_member)
360+ } )
361+ . collect( )
362+ }
363+ } else {
364+ unsafe fn message_fields_from_rosidl_message_members(
365+ message_members: & rosidl_message_members_t,
366+ num_fields: usize ,
367+ ) -> Vec <MessageFieldInfo > {
368+ // Rolling and newer distros may be built with bindings whose `MessageMember`
369+ // trailing fields differ from the runtime introspection layout, so use the stable
370+ // prefix and detect the runtime array stride before reading field names.
371+ let stride = Self :: message_member_stride( message_members) ;
372+ let base = message_members. members_ as * const u8 ;
373+ ( 0 ..num_fields)
374+ . map( |i| {
375+ let rosidl_message_member =
376+ & * ( base. add( i * stride) as * const MessageMemberPrefix ) ;
377+ MessageFieldInfo :: from_prefix( rosidl_message_member)
378+ } )
379+ . collect( )
380+ }
381+ }
382+ }
383+
254384 /// Parses the C struct containing a list of fields.
255385 // That function must be unsafe, since it is possible to safely create a garbage non-null
256386 // pointer and store it in a rosidl_message_members_t.
@@ -259,15 +389,8 @@ impl MessageStructure {
259389 ) -> Self {
260390 debug_assert ! ( !message_members. members_. is_null( ) ) ;
261391 let num_fields: usize = usize:: try_from ( message_members. member_count_ ) . unwrap ( ) ;
262- let mut fields: Vec < _ > = ( 0 ..num_fields)
263- . map ( |i| {
264- // SAFETY: This is an array as per the documentation
265- let rosidl_message_member: & rosidl_message_member_t =
266- /*unsafe*/ { & * message_members. members_ . add ( i) } ;
267- // SAFETY: This is a valid string pointer
268- MessageFieldInfo :: from ( rosidl_message_member)
269- } )
270- . collect ( ) ;
392+ let mut fields =
393+ Self :: message_fields_from_rosidl_message_members ( message_members, num_fields) ;
271394 fields. sort_by_key ( |field_info| field_info. offset ) ;
272395 // SAFETY: Immediate conversion into owned string.
273396 let namespace = /*unsafe*/ {
@@ -1153,4 +1276,112 @@ mod tests {
11531276 let _ = msg. get ( "basic_types_values" ) ;
11541277 let _ = msg. get_mut ( "basic_types_values" ) ;
11551278 }
1279+
1280+ #[ test]
1281+ fn message_member_stride_prefers_prefix_layout_when_needed ( ) {
1282+ use rosidl_typesupport_introspection_c_field_types:: * ;
1283+ use std:: { alloc:: Layout , mem:: size_of, ptr} ;
1284+
1285+ let namespace = c"test_msgs/msg" ;
1286+ let message_name = c"Compat" ;
1287+ let first_name = c"first" ;
1288+ let second_name = c"second" ;
1289+
1290+ let prefix_stride = size_of :: < MessageMemberPrefix > ( ) ;
1291+ let runtime_stride = size_of :: < rosidl_message_member_t > ( ) ;
1292+ if runtime_stride <= prefix_stride {
1293+ return ;
1294+ }
1295+
1296+ let layout = Layout :: from_size_align (
1297+ runtime_stride + prefix_stride,
1298+ std:: mem:: align_of :: < MessageMemberPrefix > ( ) ,
1299+ )
1300+ . unwrap ( ) ;
1301+ // SAFETY: The allocation is large enough and properly aligned for MessageMemberPrefix.
1302+ let bytes = unsafe { std:: alloc:: alloc_zeroed ( layout) } ;
1303+ assert ! ( !bytes. is_null( ) ) ;
1304+ let members_ptr = bytes as * mut MessageMemberPrefix ;
1305+
1306+ let first = MessageMemberPrefix {
1307+ name_ : first_name. as_ptr ( ) ,
1308+ type_id_ : rosidl_typesupport_introspection_c__ROS_TYPE_BOOLEAN as u8 ,
1309+ string_upper_bound_ : 0 ,
1310+ members_ : ptr:: null ( ) ,
1311+ is_key_ : 0 ,
1312+ is_array_ : 0 ,
1313+ array_size_ : 0 ,
1314+ is_upper_bound_ : 0 ,
1315+ offset_ : 0 ,
1316+ default_value_ : ptr:: null ( ) ,
1317+ size_function : ptr:: null ( ) ,
1318+ get_const_function : ptr:: null ( ) ,
1319+ get_function : ptr:: null ( ) ,
1320+ fetch_function : ptr:: null ( ) ,
1321+ assign_function : ptr:: null ( ) ,
1322+ resize_function : ptr:: null ( ) ,
1323+ } ;
1324+ let second = MessageMemberPrefix {
1325+ name_ : second_name. as_ptr ( ) ,
1326+ type_id_ : rosidl_typesupport_introspection_c__ROS_TYPE_UINT8 as u8 ,
1327+ string_upper_bound_ : 0 ,
1328+ members_ : ptr:: null ( ) ,
1329+ is_key_ : 0 ,
1330+ is_array_ : 0 ,
1331+ array_size_ : 0 ,
1332+ is_upper_bound_ : 0 ,
1333+ offset_ : 1 ,
1334+ default_value_ : ptr:: null ( ) ,
1335+ size_function : ptr:: null ( ) ,
1336+ get_const_function : ptr:: null ( ) ,
1337+ get_function : ptr:: null ( ) ,
1338+ fetch_function : ptr:: null ( ) ,
1339+ assign_function : ptr:: null ( ) ,
1340+ resize_function : ptr:: null ( ) ,
1341+ } ;
1342+ unsafe {
1343+ ptr:: write ( members_ptr, first) ;
1344+ // Leave the bytes at `runtime_stride` zeroed. Writing a fake runtime-stride member
1345+ // there would overlap this prefix-stride member and corrupt the candidate that the
1346+ // test expects to be selected.
1347+ ptr:: write ( bytes. add ( prefix_stride) as * mut MessageMemberPrefix , second) ;
1348+ }
1349+
1350+ cfg_if:: cfg_if! {
1351+ if #[ cfg( ros_distro = "humble" ) ] {
1352+ let message_members = crate :: rcl_bindings:: rosidl_typesupport_introspection_c__MessageMembers_s {
1353+ message_namespace_: namespace. as_ptr( ) ,
1354+ message_name_: message_name. as_ptr( ) ,
1355+ member_count_: 2 ,
1356+ size_of_: 2 ,
1357+ members_: members_ptr as * const rosidl_message_member_t,
1358+ init_function: None ,
1359+ fini_function: None ,
1360+ } ;
1361+ } else {
1362+ let message_members = crate :: rcl_bindings:: rosidl_typesupport_introspection_c__MessageMembers_s {
1363+ message_namespace_: namespace. as_ptr( ) ,
1364+ message_name_: message_name. as_ptr( ) ,
1365+ member_count_: 2 ,
1366+ size_of_: 2 ,
1367+ has_any_key_member_: false ,
1368+ members_: members_ptr as * const rosidl_message_member_t,
1369+ init_function: None ,
1370+ fini_function: None ,
1371+ } ;
1372+ }
1373+ } ;
1374+
1375+ assert_eq ! (
1376+ unsafe { MessageStructure :: message_member_stride( & message_members) } ,
1377+ size_of:: <MessageMemberPrefix >( )
1378+ ) ;
1379+
1380+ let second_prefix = unsafe { & * ( bytes. add ( prefix_stride) as * const MessageMemberPrefix ) } ;
1381+ let field_info = unsafe { MessageFieldInfo :: from_prefix ( second_prefix) } ;
1382+ assert_eq ! ( field_info. name, "second" ) ;
1383+ assert_eq ! ( field_info. base_type, BaseType :: Uint8 ) ;
1384+
1385+ unsafe { std:: alloc:: dealloc ( bytes, layout) } ;
1386+ }
11561387}
0 commit comments