mujoco_rs/
util.rs

1//! Utility related data
2use std::{marker::PhantomData, ops::{Deref, DerefMut}};
3
4
5/// Creates a (start, length) tuple based on
6/// lookup variables that define the mapping in MuJoCo's mjModel struct.
7/// The tuple is used to create views to correct addresses in corresponding structs.
8/// Format: item id, map from item id to index inside the array of all items' values,
9///         number of items, maximum number of elements inside the array of all items' values
10#[macro_export]
11#[doc(hidden)]
12macro_rules! mj_view_indices {
13    ($id:expr, $addr_map:expr, $njnt:expr, $max_n:expr) => {
14        {
15            let start_addr = *$addr_map.add($id) as isize;
16            if start_addr == -1 {
17                (0, 0)
18            }
19            else
20            {
21                let end_addr = if $id + 1 < $njnt as usize {*$addr_map.add($id as usize + 1) as usize} else {$max_n as usize};
22                let n = end_addr - start_addr as usize;
23                (start_addr as usize, n)
24            }
25        }
26    };
27}
28
29/// Returns the correct address mapping based on the X in nX (nq, nv, nu, ...).
30#[macro_export]
31#[doc(hidden)]
32macro_rules! mj_model_nx_to_mapping {
33    ($model_ffi:ident, nq) => {
34        $model_ffi.jnt_qposadr
35    };
36
37    ($model_ffi:ident, nv) => {
38        $model_ffi.jnt_dofadr
39    };
40
41    ($model_ffi:ident, nsensordata) => {
42        $model_ffi.sensor_adr
43    };
44    ($model_ffi:ident, ntupledata) => {
45        $model_ffi.tuple_adr
46    };
47    ($model_ffi:ident, ntexdata) => {
48        $model_ffi.tex_adr
49    };
50    ($model_ffi:ident, nnumericdata) => {
51        $model_ffi.numeric_adr
52    };
53    ($model_ffi:ident, nhfielddata) => {
54        $model_ffi.hfield_adr
55    };
56}
57
58
59/// Returns the correct number of items based on the X in nX (nq, nv, nu, ...).
60#[macro_export]
61#[doc(hidden)]
62macro_rules! mj_model_nx_to_nitem {
63    ($model_ffi:ident, nq) => {
64        $model_ffi.njnt
65    };
66
67    ($model_ffi:ident, nv) => {
68        $model_ffi.njnt
69    };
70
71    ($model_ffi:ident, nsensordata) => {
72        $model_ffi.nsensor
73    };
74    ($model_ffi:ident, ntupledata) => {
75        $model_ffi.ntuple
76    };
77    ($model_ffi:ident, ntexdata) => {
78        $model_ffi.ntex
79    };
80    ($model_ffi:ident, nnumericdata) => {
81        $model_ffi.nnumeric
82    };
83    ($model_ffi:ident, nhfielddata) => {
84        $model_ffi.nhfield
85    };
86}
87
88/// Provides a more direct view to a C array.
89/// # Safety
90/// This does not check if the data is valid. It is assumed
91/// the correct data is given and that it doesn't get dropped before this struct.
92/// This does not break Rust's checks as we create the view each
93/// time from the saved pointers.
94/// This should ONLY be used within a wrapper who fully encapsulates the underlying data.
95#[derive(Debug)]
96pub struct PointerViewMut<'d, T> {
97    ptr: *mut T,
98    len: usize,
99    phantom: PhantomData<&'d mut ()>
100}
101
102impl<'d, T> PointerViewMut<'d, T> {
103    pub(crate) fn new(ptr: *mut T, len: usize) -> Self {
104        Self {ptr, len, phantom: PhantomData}
105    }
106
107    #[allow(unused)]
108    pub(crate) unsafe fn set_len(&mut self, len: usize) {
109        self.len = len;
110    }
111}
112
113/// Compares if the two views point to the same data.
114impl<T> PartialEq for PointerViewMut<'_, T> {
115    fn eq(&self, other: &Self) -> bool {
116        self.ptr == other.ptr  // if the pointer differs, this isn't a view to the same data
117    }
118}
119
120impl<T> Deref for PointerViewMut<'_, T> {
121    type Target = [T];
122    fn deref(&self) -> &Self::Target {
123        unsafe { std::slice::from_raw_parts(self.ptr, self.len) }
124    }
125}
126
127impl<T> DerefMut for PointerViewMut<'_, T> {
128    fn deref_mut(&mut self) -> &mut Self::Target {
129        unsafe { std::slice::from_raw_parts_mut(self.ptr, self.len) }
130    }
131}
132
133/// Provides a more direct view to a C array.
134/// # Safety
135/// This does not check if the data is valid. It is assumed
136/// the correct data is given and that it doesn't get dropped before this struct.
137/// This does not break Rust's checks as we create the view each
138/// time from the saved pointers.
139/// This should ONLY be used within a wrapper who fully encapsulates the underlying data.
140#[derive(Debug)]
141pub struct PointerView<'d, T> {
142    ptr: *const T,
143    len: usize,
144    phantom: PhantomData<&'d ()>
145}
146
147impl<'d, T> PointerView<'d, T> {
148    pub(crate) fn new(ptr: *const T, len: usize) -> Self {
149        Self {ptr, len, phantom: PhantomData}
150    }
151    
152    #[allow(unused)]
153    pub(crate) unsafe fn set_len(&mut self, len: usize) {
154        self.len = len;
155    }
156}
157
158/// Compares if the two views point to the same data.
159impl<T> PartialEq for PointerView<'_, T> {
160    fn eq(&self, other: &Self) -> bool {
161        self.ptr == other.ptr  // if the pointer differs, this isn't a view to the same data
162    }
163}
164
165impl<T> Deref for PointerView<'_, T> {
166    type Target = [T];
167    fn deref(&self) -> &Self::Target {
168        unsafe { std::slice::from_raw_parts(self.ptr, self.len) }
169    }
170}
171
172/***************************/
173//  Evaluation helper macro
174/***************************/
175/// When @eval is given false, ignore the given contents.
176/// In other cases, expand the given contents.
177#[macro_export]
178#[doc(hidden)]
179macro_rules! eval_or_expand {
180    (@eval $(true)? { $($data:tt)* } ) => { $($data)* };
181    (@eval false { $($data:tt)* } ) => {};
182}
183
184
185/**************************************************************************************************/
186// View creation for MjData and MjModel
187/**************************************************************************************************/
188
189/// Creates a $view struct, mapping $field and $opt_field to the same location as in $data.
190#[macro_export]
191#[doc(hidden)]
192macro_rules! view_creator {
193    /* Pointer view */
194    ($self:expr, $view:ident, $data:expr, [$($field:ident),*], [$($opt_field:ident),*], $ptr_view:expr) => {
195        unsafe {
196            $view {
197                $(
198                    $field: $ptr_view($data.$field.add($self.$field.0).cast(), $self.$field.1),
199                )*
200                $(
201                    $opt_field: if $self.$opt_field.1 > 0 {
202                        Some($ptr_view($data.$opt_field.add($self.$opt_field.0).cast(), $self.$opt_field.1))
203                    } else {None},
204                )*
205            }
206        }
207    };
208
209    ($self:expr, $view:ident, $data:expr, $prefix:ident, [$($field:ident),*], [$($opt_field:ident),*], $ptr_view:expr) => {
210        paste::paste! {
211            unsafe {
212                $view {
213                    $(
214                        $field: $ptr_view($data.[<$prefix $field>].add($self.$field.0).cast(), $self.$field.1),
215                    )*
216                    $(
217                        $opt_field: if $self.$opt_field.1 > 0 {
218                            Some($ptr_view($data.[<$prefix $opt_field>].add($self.$opt_field.0).cast(), $self.$opt_field.1))
219                        } else {None},
220                    )*
221                }
222            }
223        }
224    };
225}
226
227
228/// Macro for reducing duplicated code when creating info structs to
229/// items in [`MjData`](crate::prelude::MjData) or [`MjModel`](crate::prelude::MjModel).
230/// This creates a method `X(self, name; &str) -> XInfo`.
231/// Compatible entries: (..., [name: fixed number], [name: ffi().attribute (* repeats)], [length of the item's data array (e.g., hfield -> nhfielddata, texture -> ntexdata)])
232#[doc(hidden)]
233#[macro_export]
234macro_rules! info_method {
235    ($info_type:ident, $ffi:expr, $type_:ident, [$($attr:ident: $len:expr),*], [$($attr_ffi:ident: $len_ffi:ident $(* $multiplier:expr)?),*], [$($attr_dyn:ident: $ffi_len_dyn:expr),*]) => {
236        paste::paste! {
237            #[doc = concat!(
238                "Obtains a [`", stringify!([<Mj $type_:camel $info_type Info>]), "`] struct containing information about the name, id, and ",
239                "indices required for obtaining references to the correct locations in [`Mj", stringify!($info_type), "`]. ",
240                "The actual view can be obtained via [`", stringify!([<Mj $type_:camel $info_type Info>]), "::view`].\n",
241                "# Panics\n",
242                "A panic will occur if `name` contains `\\0` characters."
243            )]
244            pub fn $type_(&self, name: &str) -> Option<[<Mj $type_:camel $info_type Info>]> {
245                let c_name = CString::new(name).unwrap();
246                let ffi = self.$ffi;
247                let id = unsafe { mj_name2id(ffi, MjtObj::[<mjOBJ_ $type_:upper>] as i32, c_name.as_ptr())};
248                if id == -1 {  // not found
249                    return None;
250                }
251
252                let id = id as usize;
253                $(
254                    let $attr = (id * $len, $len);
255                )*
256
257                $(
258                    let $attr_ffi = (id * ffi.$len_ffi as usize $( * $multiplier)*, ffi.$len_ffi as usize $( * $multiplier)*);
259                )*
260
261                $(
262                    let $attr_dyn = unsafe { mj_view_indices!(
263                        id,
264                        mj_model_nx_to_mapping!(ffi, $ffi_len_dyn),
265                        mj_model_nx_to_nitem!(ffi, $ffi_len_dyn),
266                        ffi.$ffi_len_dyn
267                    ) };
268                )*
269
270                Some([<Mj $type_:camel $info_type Info>] {name: name.to_string(), id, $($attr,)* $($attr_ffi,)* $($attr_dyn),*})
271            }
272        }
273    }
274}
275
276
277/// This creates a method `X(self, name: &str) -> XInfo`.
278/// 
279/// # Compatible Entry Types
280/// The macro supports the following types of entries for struct fields:
281/// 
282/// - **Fixed number**:  
283///   `[field_name: fixed_length]`.  
284///   Example: `[id: 1]` (for a field with a fixed length of 1)
285/// 
286/// - **FFI attribute (possibly with multiplier)**:  
287///   `[field_name: ffi_attribute (* multiplier)]`.  
288///   Example: `[matid: nmatid * 2]` (where `nmatid` is an attribute from the FFI struct, and the field length is `nmatid * 2`)
289/// 
290/// - **Dynamic length (from item's data array)**:  
291///   `[field_name: data_array_length]`.  
292///   Example: `[hfielddata: nhfielddata]` (where `nhfielddata` is the length of the hfield data array).
293///   Note: nhfielddata is the major index (i.e., [nhfielddata x something] in the C array.)
294///
295#[doc(hidden)]
296#[macro_export]
297macro_rules! info_with_view {
298    /* PointerView */
299
300    /* name of the view/info, attribute prefix in MjData/MjModel, [attributes always present], [attributes that can be None] */
301    ($info_type:ident, $name:ident, $prefix:ident, [$($attr:ident: $type_:ty),*], [$($opt_attr:ident: $type_opt:ty),*]$(,$generics:ty: $bound:ty)?) => {
302        paste::paste! {
303            #[doc = "Stores information required to create views to [`Mj" $info_type "`] arrays corresponding to a " $name "."]
304            #[allow(non_snake_case)]
305            pub struct [<Mj $name:camel $info_type Info>] {
306                pub name: String,
307                pub id: usize,
308                $(
309                    $attr: (usize, usize),
310                )*
311                $(
312                    $opt_attr: (usize, usize),
313                )*
314            }
315
316            impl [<Mj $name:camel $info_type Info>] {
317                #[doc = "Returns a mutable view to the correct fields in [`Mj" $info_type "`]"]
318                pub fn view_mut $(<$generics: $bound>)?(&self, [<$info_type:lower>]: &mut [<Mj $info_type>]$(<$generics>)?) -> [<Mj $name:camel $info_type ViewMut>]<'_> {
319                    view_creator!(self, [<Mj $name:camel $info_type ViewMut>], [<$info_type:lower>].ffi(), $prefix, [$($attr),*], [$($opt_attr),*], crate::util::PointerViewMut::new)
320                }
321
322                #[doc = "Returns a view to the correct fields in [`Mj" $info_type "`]"]
323                pub fn view $(<$generics: $bound>)?(&self, [<$info_type:lower>]: &[<Mj $info_type>]$(<$generics>)?) -> [<Mj $name:camel $info_type View>]<'_> {
324                    view_creator!(self, [<Mj $name:camel $info_type View>], [<$info_type:lower>].ffi(), $prefix, [$($attr),*], [$($opt_attr),*], crate::util::PointerView::new)
325                }
326            }
327
328            #[doc = "A mutable view to " $name " variables of [`Mj" $info_type "`]."]
329            #[allow(non_snake_case)]
330            pub struct [<Mj $name:camel $info_type ViewMut>]<'d> {
331                $(
332                    pub $attr: crate::util::PointerViewMut<'d, $type_>,
333                )*
334                $(
335                    pub $opt_attr: Option<crate::util::PointerViewMut<'d, $type_opt>>,
336                )*
337            }
338
339            impl [<Mj $name:camel $info_type ViewMut>]<'_> {
340                /// Resets the internal variables to 0.0.
341                pub fn zero(&mut self) {
342                    $(
343                        self.$attr.fill(unsafe { std::mem::zeroed() });
344                    )*
345                    $(
346                        if let Some(x) = &mut self.$opt_attr {
347                            x.fill(unsafe { std::mem::zeroed() });
348                        }
349                    )*
350                }
351            }
352
353            #[doc = "An immutable view to " $name " variables of [`Mj" $info_type "`]."]
354            #[allow(non_snake_case)]
355            pub struct [<Mj $name:camel $info_type View>]<'d> {
356                $(
357                    pub $attr: crate::util::PointerView<'d, $type_>,
358                )*
359                $(
360                    pub $opt_attr: Option<crate::util::PointerView<'d, $type_opt>>,
361                )*
362            }
363        }
364    };
365
366    /* name of the view/info, [attributes always present], [attributes that can be None] */
367    ($info_type:ident, $name:ident, [$($attr:ident: $type_:ty),*], [$($opt_attr:ident: $type_opt:ty),*]$(,$generics:ty: $bound:ty)?) => {
368        paste::paste! {
369            #[doc = "Stores information required to create views to [`Mj" $info_type "`] arrays corresponding to a " $name "."]
370            #[allow(non_snake_case)]
371            pub struct [<Mj $name:camel $info_type Info>] {
372                pub name: String,
373                pub id: usize,
374                $(
375                    $attr: (usize, usize),
376                )*
377                $(
378                    $opt_attr: (usize, usize),
379                )*
380            }
381
382            impl [<Mj $name:camel $info_type Info>] {
383                #[doc = "Returns a mutable view to the correct fields in [`Mj" $info_type "`]"]
384                pub fn view_mut $(<$generics: $bound>)?(&self, [<$info_type:lower>]: &mut [<Mj $info_type>]$(<$generics>)?) -> [<Mj $name:camel $info_type ViewMut>]<'_> {
385                    view_creator!(self, [<Mj $name:camel $info_type ViewMut>], [<$info_type:lower>].ffi(), [$($attr),*], [$($opt_attr),*], crate::util::PointerViewMut::new)
386                }
387
388                #[doc = "Returns a view to the correct fields in [`Mj" $info_type "`]"]
389                pub fn view $(<$generics: $bound>)?(&self, [<$info_type:lower>]: &[<Mj $info_type>]$(<$generics>)?) -> [<Mj $name:camel $info_type View>]<'_> {
390                    view_creator!(self, [<Mj $name:camel $info_type View>], [<$info_type:lower>].ffi(), [$($attr),*], [$($opt_attr),*], crate::util::PointerView::new)
391                }
392            }
393
394            #[doc = "A mutable view to " $name " variables of [`Mj" $info_type "`]."]
395            #[allow(non_snake_case)]
396            pub struct [<Mj $name:camel $info_type ViewMut>]<'d> {
397                $(
398                    pub $attr: crate::util::PointerViewMut<'d, $type_>,
399                )*
400                $(
401                    pub $opt_attr: Option<crate::util::PointerViewMut<'d, $type_opt>>,
402                )*
403            }
404
405            impl [<Mj $name:camel $info_type ViewMut>]<'_> {
406                /// Resets the internal variables to 0.0.
407                pub fn zero(&mut self) {
408                    $(
409                        self.$attr.fill(unsafe { std::mem::zeroed() });
410                    )*
411                    $(
412                        if let Some(x) = &mut self.$opt_attr {
413                            x.fill(unsafe { std::mem::zeroed() });
414                        }
415                    )*
416                }
417            }
418
419            #[doc = "An immutable view to " $name " variables of [`Mj" $info_type "`]."]
420            #[allow(non_snake_case)]
421            pub struct [<Mj $name:camel $info_type View>]<'d> {
422                $(
423                    pub $attr: crate::util::PointerView<'d, $type_>,
424                )*
425                $(
426                    pub $opt_attr: Option<crate::util::PointerView<'d, $type_opt>>,
427                )*
428            }
429        }
430    };
431}
432
433
434#[doc(hidden)]
435#[macro_export]
436macro_rules! getter_setter {
437    (get, [$($([$ffi:ident])? $name:ident $(+ $symbol:tt)?: bool; $comment:expr);* $(;)?]) => {paste::paste!{
438        $(
439            #[doc = concat!("Check ", $comment)]
440            pub fn [<$name:camel:snake $($symbol)?>](&self) -> bool {
441                self$(.$ffi())?.$name == 1
442            }
443        )*
444    }};
445
446    (get, [$($([$ffi:ident $(,$ffi_mut:ident)?])? $((allow_mut = $cfg_mut:literal))? $name:ident $(+ $symbol:tt)?: & $type:ty; $comment:expr);* $(;)?]) => {paste::paste!{
447        $(
448            #[doc = concat!("Return an immutable reference to ", $comment)]
449            pub fn [<$name:camel:snake $($symbol)?>](&self) -> &$type {
450                &self$(.$ffi())?.$name
451            }
452
453            crate::eval_or_expand! {
454                @eval $($cfg_mut)? {
455                    #[doc = concat!("Return a mutable reference to ", $comment)]
456                    pub fn [<$name:camel:snake _mut>](&mut self) -> &mut $type {
457                        #[allow(unused_unsafe)]
458                        unsafe { &mut self$(.$($ffi_mut())?)?.$name }
459                    }
460                }
461            }
462        )*
463    }};
464
465    (get, [$($([$ffi:ident])? $name:ident $(+ $symbol:tt)?: $type:ty; $comment:expr);* $(;)?]) => {paste::paste!{
466        $(
467            #[doc = concat!("Return value of ", $comment)]
468            pub fn [<$name:camel:snake $($symbol)?>](&self) -> $type {
469                self$(.$ffi())?.$name.into()
470            }
471        )*
472    }};
473
474    (set, [$($([$ffi_mut:ident])? $name:ident: $type:ty; $comment:expr);* $(;)?]) => {
475        paste::paste!{ 
476            $(
477                #[doc = concat!("Set ", $comment)]
478                pub fn [<set_ $name:camel:snake>](&mut self, value: $type) {
479                    #[allow(unused_unsafe)]
480                    unsafe { self$(.$ffi_mut())?.$name = value.into() };
481                }
482            )*
483        }
484    };
485
486    /* Enum conversion */
487    (force!, get, [$($([$ffi:ident])? $name:ident $(+ $symbol:tt)? : $type:ty; $comment:expr);* $(;)?]) => {paste::paste!{
488        $(
489            #[doc = concat!("Return value of ", $comment)]
490            pub fn [<$name:camel:snake $($symbol)?>](&self) -> $type {
491                unsafe { std::mem::transmute(self$(.$ffi())?.$name) }
492            }
493        )*
494    }};
495
496    (force!, set, [$($([$ffi_mut:ident])? $name:ident: $type:ty; $comment:expr);* $(;)?]) => {
497        paste::paste!{ 
498            $(
499                #[doc = concat!("Set ", $comment)]
500                pub fn [<set_ $name:camel:snake>](&mut self, value: $type) {
501                    #[allow(unnecessary_transmutes)]
502                    unsafe { self$(.$ffi_mut())?.$name = std::mem::transmute(value) };
503                }
504            )*
505        }
506    };
507
508    /* Builder pattern */
509    (force!, with, [$($([$ffi_mut:ident])? $name:ident: $type:ty; $comment:expr);* $(;)?]) => {
510        paste::paste!{ 
511            $(
512                #[doc = concat!("Builder method for setting ", $comment)]
513                pub fn [<with_ $name:camel:snake>](mut self, value: $type) -> Self {
514                    #[allow(unnecessary_transmutes)]
515                    unsafe { self$(.$ffi_mut())?.$name = std::mem::transmute(value) };
516                    self
517                }
518            )*
519        }
520    };
521
522    (force!, [&] with, [$($([$ffi_mut:ident])? $name:ident: $type:ty; $comment:expr);* $(;)?]) => {
523        paste::paste!{ 
524            $(
525                #[doc = concat!("Builder method for setting ", $comment)]
526                pub fn [<with_ $name:camel:snake>](&mut self, value: $type) -> &mut Self {
527                    #[allow(unnecessary_transmutes)]
528                    unsafe { self$(.$ffi_mut())?.$name = std::mem::transmute(value) };
529                    self
530                }
531            )*
532        }
533    };
534
535    (with, [$($([$ffi_mut:ident])? $name:ident: $type:ty; $comment:expr);* $(;)?]) => {
536        paste::paste!{ 
537            $(
538                #[doc = concat!("Builder method for setting ", $comment)]
539                pub fn [<with_ $name:camel:snake>](mut self, value: $type) -> Self {
540                    #[allow(unused_unsafe)]
541                    unsafe { self$(.$ffi_mut())?.$name = value.into() };
542                    self
543                }
544            )*
545        }
546    };
547
548    ([&] with, [$($([$ffi_mut:ident])? $name:ident: $type:ty; $comment:expr);* $(;)?]) => {
549        paste::paste!{ 
550            $(
551                #[doc = concat!("Builder method for setting ", $comment)]
552                pub fn [<with_ $name:camel:snake>](&mut self, value: $type) -> &mut Self {
553                    #[allow(unused_unsafe)]
554                    unsafe { self$(.$ffi_mut())?.$name = value.into() };
555                    self
556                }
557            )*
558        }
559    };
560    
561    /* Handling of optional arguments */
562    /* Enum pass */
563    (force!, get, set, [ $($([$ffi: ident, $ffi_mut:ident])? $name:ident $(+ $symbol:tt)? : $type:ty ; $comment:expr );* $(;)?]) => {
564        $crate::getter_setter!(force!, get, [ $($([$ffi])? $name $(+ $symbol)? : $type ; $comment );* ]);
565        $crate::getter_setter!(force!, set, [ $($([$ffi_mut])? $name : $type ; $comment );* ]);
566    };
567
568    (get, set, [ $($([$ffi: ident, $ffi_mut:ident])? $name:ident $(+ $symbol:tt)? : bool ; $comment:expr );* $(;)?]) => {
569        $crate::getter_setter!(get, [ $($([$ffi])? $name $(+ $symbol)? : bool ; $comment );* ]);
570        $crate::getter_setter!(set, [ $($([$ffi_mut])? $name : bool ; $comment );* ]);
571    };
572
573    (get, set, [ $($([$ffi: ident, $ffi_mut:ident])? $name:ident $(+ $symbol:tt)? : $type:ty ; $comment:expr );* $(;)?]) => {
574        $crate::getter_setter!(get, [ $($([$ffi])? $name $(+ $symbol)? : $type ; $comment );* ]);
575        $crate::getter_setter!(set, [ $($([$ffi_mut])? $name : $type ; $comment );* ]);
576    };
577
578    /* Builder pattern */
579    ($([$token:tt])? with, get, set, [ $($([$ffi: ident, $ffi_mut:ident])? $name:ident $(+ $symbol:tt)? : bool ; $comment:expr );* $(;)?]) => {
580        $crate::getter_setter!(get, [ $($([$ffi])? $name $(+ $symbol)? : bool ; $comment );* ]);
581        $crate::getter_setter!(set, [ $($([$ffi_mut])? $name : bool ; $comment );* ]);
582        $crate::getter_setter!($([$token])? with, [ $($([$ffi_mut])? $name : bool ; $comment );* ]);
583    };
584
585    ($([$token:tt])? with, get, set, [ $($([$ffi: ident, $ffi_mut:ident])? $name:ident $(+ $symbol:tt)? : $type:ty ; $comment:expr );* $(;)?]) => {
586        $crate::getter_setter!(get, [ $($([$ffi])? $name $(+ $symbol)?: $type ; $comment );* ]);
587        $crate::getter_setter!(set, [ $($([$ffi_mut])? $name : $type ; $comment );* ]);
588        $crate::getter_setter!($([$token])? with, [ $($([$ffi_mut])? $name : $type ; $comment );* ]);
589    };
590
591    (force!, $([$token:tt])? with, get, set, [ $($([$ffi: ident, $ffi_mut:ident])? $name:ident $(+ $symbol:tt)? : $type:ty ; $comment:expr );* $(;)?]) => {
592        $crate::getter_setter!(force!, get, [$($([$ffi])? $name $(+ $symbol)? : $type ; $comment );* ]);
593        $crate::getter_setter!(force!, set, [$($([$ffi_mut])? $name : $type ; $comment );* ]);
594        $crate::getter_setter!(force!, $([$token])? with, [$($([$ffi_mut])? $name : $type ; $comment );* ]);
595    };
596
597    ($([$token:tt])? with, get, [$( $([$ffi: ident, $ffi_mut:ident])? $((allow_mut = $allow_mut:literal))? $name:ident $(+ $symbol:tt)? : & $type:ty ; $comment:expr );* $(;)?]) => {
598        $crate::getter_setter!(get, [ $($([$ffi, $ffi_mut])? $((allow_mut = $allow_mut))? $name $(+ $symbol)? : & $type ; $comment );* ]);
599        $crate::getter_setter!($([$token])? with, [ $( $([$ffi_mut])? $name : $type ; $comment );* ]);
600    };
601}
602
603
604#[doc(hidden)]
605#[macro_export]
606/// Constructs builder methods.
607macro_rules! builder_setters {
608    ($($name:ident: $type:ty; $comment:expr);* $(;)?) => {
609        $(
610            #[doc = concat!("Set ", $comment)]
611            pub fn $name(mut self, value: $type) -> Self {
612                self.$name = value;
613                self
614            }
615        )*
616    };
617}
618
619/// A macro for creating a slice over a raw array of dynamic size (given by some other variable in $len_accessor).
620/// Syntax: attribute: <optional pre-transformations (e.g., `as_ptr as_mut_ptr`)> &[datatype; documentation string;
621/// code to access the length attribute, appearing after `self.`]
622/// Syntax for arrays whose size is a sum from some length array:
623///     summed {
624///         ...
625///         attribute: &[datatype; documentation; [
626///                 size multiplier;
627///                 (code to access the length array, appearing after self);
628///                 (code to access the length array's length, appearing after self)
629///             ]
630///         ],
631///         ...
632///     }
633///
634#[doc(hidden)]
635#[macro_export]
636macro_rules! array_slice_dyn {
637    // Arrays that are of scalar variable size
638    ($($((allow_mut = $cfg_mut:literal))? $name:ident: $($as_ptr:ident $as_mut_ptr:ident)? &[$type:ty $([$cast:ident])?; $doc:literal; $($len_accessor:tt)*]),*) => {
639        paste::paste! {
640            $(
641                #[doc = concat!("Immutable slice of the ", $doc," array.")]
642                pub fn [<$name:camel:snake>](&self) -> &[$type] {
643                    let length = self.$($len_accessor)* as usize;
644                    if length == 0 {
645                        return &[];
646                    }
647                    unsafe { std::slice::from_raw_parts(self.ffi().$name$(.$as_ptr())?$(.$cast())? as *const _, length) }
648                }
649
650                crate::eval_or_expand! {
651                    @eval $($cfg_mut)? {
652                        #[doc = concat!("Mutable slice of the ", $doc," array.")]
653                        pub fn [<$name:camel:snake _mut>](&mut self) -> &mut [$type] {
654                            let length = self.$($len_accessor)* as usize;
655                            if length == 0 {
656                                return &mut [];
657                            }
658                            unsafe { std::slice::from_raw_parts_mut(self.ffi_mut().$name$(.$as_mut_ptr())?$(.$cast())?, length) }
659                        }
660                    }
661                }
662            )*
663        }
664    };
665
666    // Arrays that are of summed variable size
667    (summed { $( $(allow_mut = $cfg_mut:literal)? $name:ident: &[$type:ty; $doc:literal; [$multiplier:literal ; ($($len_array:tt)*) ; ($($len_array_length:tt)*)]]),* }) => {
668        paste::paste! {
669            $(
670                #[doc = concat!("Immutable slice of the ", $doc," array.")]
671                pub fn [<$name:camel:snake>](&self) -> &[[$type; $multiplier]] {
672                    // Obtain a slice to the length array.
673                    let length_array_length = self.$($len_array_length)* as usize;
674                    if length_array_length == 0 {
675                        return &[];
676                    }
677
678                    let length = unsafe { std::slice::from_raw_parts(
679                        self.$($len_array)*.cast(),
680                        length_array_length
681                    ).into_iter().sum::<u32>() as usize };
682
683                    if length == 0 {
684                        return &[];
685                    }
686
687                    unsafe { std::slice::from_raw_parts(self.ffi().$name.cast(), length) }
688                }
689                
690                crate::eval_or_expand! {
691                    @eval $($cfg_mut)? {
692                        #[doc = concat!("Mutable slice of the ", $doc," array.")]
693                        pub fn [<$name:camel:snake _mut>](&mut self) -> &mut [[$type; $multiplier]] {
694                            let length_array_length = self.$($len_array_length)* as usize;
695                            if length_array_length == 0 {
696                                return &mut [];
697                            }
698
699                            let length = unsafe { std::slice::from_raw_parts(
700                                self.$($len_array)*.cast(),
701                                length_array_length
702                            ).into_iter().sum::<u32>() as usize };
703
704                            if length == 0 {
705                                return &mut [];
706                            }
707
708                            unsafe { std::slice::from_raw_parts_mut(self.ffi_mut().$name.cast(), length) }
709                        }
710                    }
711                }
712            )*
713        }
714    };
715
716    // Arrays whose second dimension is dependent on some variable
717    (sublen_dep {$( $(allow_mut = $cfg_mut:literal)? $name:ident: $($as_ptr:ident $as_mut_ptr:ident)? &[[$type:ty; $($inner_len_accessor:tt)*] $([$cast:ident])?; $doc:literal; $($len_accessor:tt)*]),*}) => {
718        paste::paste! {
719            $(
720                #[doc = concat!("Immutable slice of the ", $doc," array.")]
721                pub fn [<$name:camel:snake>](&self) -> &[$type] {
722                    let length = self.$($len_accessor)* as usize * self.$($inner_len_accessor)* as usize;
723                    if length == 0 {
724                        return &[];
725                    }
726
727
728                    unsafe { std::slice::from_raw_parts(self.ffi().$name$(.$as_ptr())?$(.$cast())? as *const _, length) }
729                }
730
731                crate::eval_or_expand! {
732                    @eval $($cfg_mut)? {
733                        #[doc = concat!("Mutable slice of the ", $doc," array.")]
734                        pub fn [<$name:camel:snake _mut>](&mut self) -> &mut [$type] {
735                            let length = self.$($len_accessor)* as usize * self.$($inner_len_accessor)* as usize;
736                            if length == 0 {
737                                return &mut [];
738                            }
739                            unsafe { std::slice::from_raw_parts_mut(self.ffi_mut().$name$(.$as_mut_ptr())?$(.$cast())?, length) }
740                        }
741                    }
742                }
743            )*
744        }
745    };
746}
747
748/// View a raw C-string (null terminated) as a Rust string slice.
749#[doc(hidden)]
750#[macro_export]
751macro_rules! c_str_as_str_method {
752    (raw {$( $name:ident : $comment:literal; )*}) => {
753        $(
754            #[doc = $comment]
755            pub fn $name(&self) -> &str {
756                unsafe { 
757                    let c_ptr = self.ffi().$name;
758                    CStr::from_ptr(c_ptr).to_str().unwrap()
759                }
760            }
761        )*
762    };
763}
764
765/// assert_eq!, but with tolerance for floating point rounding.
766#[doc(hidden)]
767#[macro_export]
768macro_rules! assert_relative_eq {
769    ($a:expr, $b:expr, epsilon = $eps:expr) => {{
770        let (a, b, eps) = ($a as f64, $b as f64, $eps as f64);
771        assert!((a - b).abs() <= eps, "left={:?} right={:?} eps={:?}", a, b, eps);
772    }};
773}