// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
+/// Reexport for `local_inner_macros`; see
+/// <https://doc.rust-lang.org/edition-guide/rust-2018/macros/macro-changes.html#macros-using-local_inner_macros>.
+#[doc(hidden)]
+#[macro_export]
+macro_rules! _memoffset__compile_error {
+ ($($inner:tt)*) => {
+ compile_error! { $($inner)* }
+ }
+}
+
/// Produces a range instance representing the sub-slice containing the specified member.
///
/// This macro provides 2 forms of differing functionalities.
///
-/// The first form is identical to the appearance of the `offset_of!` macro,
-/// and just like `offset_of!`, it has no limit on the depth of fields / subscripts used.
+/// The first form is identical to the appearance of the `offset_of!` macro.
///
/// ```ignore
-/// span_of!(Struct, member[index].field)
+/// span_of!(Struct, member)
/// ```
///
/// The second form of `span_of!` returns a sub-slice which starts at one field, and ends at another.
/// span_of!(Struct, start ..)
/// ```
///
-/// *Note*:
-/// This macro uses recursion in order to resolve the range expressions, so there is a limit to the complexity of the expression.
+/// *Note*:
+/// This macro uses recursion in order to resolve the range expressions, so there is a limit to
+/// the complexity of the expression.
/// In order to raise the limit, the compiler's recursion limit should be lifted.
///
-/// *Note*:
-/// This macro may not make much sense when used on structs that are not `#[repr(C, packed)]`
-///
/// ## Examples
/// ```
/// #[macro_use]
/// extern crate memoffset;
///
-/// #[repr(C, packed)]
+/// #[repr(C)]
/// struct Florp {
/// a: u32
/// }
///
-/// #[repr(C, packed)]
+/// #[repr(C)]
/// struct Blarg {
-/// x: u64,
+/// x: [u32; 2],
/// y: [u8; 56],
/// z: Florp,
/// egg: [[u8; 4]; 4]
/// }
///
/// fn main() {
+/// assert_eq!(0..84, span_of!(Blarg, ..));
+/// assert_eq!(0..8, span_of!(Blarg, .. y));
+/// assert_eq!(0..64, span_of!(Blarg, ..= y));
/// assert_eq!(0..8, span_of!(Blarg, x));
-/// assert_eq!(64..68, span_of!(Blarg, z.a));
-/// assert_eq!(79..80, span_of!(Blarg, egg[2][3]));
-///
-/// assert_eq!(8..64, span_of!(Blarg, y[0] .. z));
-/// assert_eq!(0..42, span_of!(Blarg, x .. y[34]));
-/// assert_eq!(0..64, span_of!(Blarg, x ..= y));
-/// assert_eq!(58..68, span_of!(Blarg, y[50] ..= z));
+/// assert_eq!(8..84, span_of!(Blarg, y ..));
+/// assert_eq!(0..8, span_of!(Blarg, x .. y));
+/// assert_eq!(0..64, span_of!(Blarg, x ..= y));
/// }
/// ```
-#[macro_export]
+#[macro_export(local_inner_macros)]
macro_rules! span_of {
- (@helper $root:ident, [] ..=) => {
- compile_error!("Expected a range, found '..='")
+ (@helper $root:ident, [] ..=) => {
+ _memoffset__compile_error!("Expected a range, found '..='")
};
(@helper $root:ident, [] ..) => {
- compile_error!("Expected a range, found '..'")
+ _memoffset__compile_error!("Expected a range, found '..'")
};
- (@helper $root:ident, [] ..= $($field:tt)+) => {
- (&$root as *const _ as usize,
- &$root.$($field)* as *const _ as usize + $crate::mem::size_of_val(&$root.$($field)*))
- };
- (@helper $root:ident, [] .. $($field:tt)+) => {
- (&$root as *const _ as usize, &$root.$($field)* as *const _ as usize)
- };
- (@helper $root:ident, $(# $begin:tt)+ [] ..= $($end:tt)+) => {
- (&$root.$($begin)* as *const _ as usize,
- &$root.$($end)* as *const _ as usize + $crate::mem::size_of_val(&$root.$($end)*))
- };
- (@helper $root:ident, $(# $begin:tt)+ [] .. $($end:tt)+) => {
- (&$root.$($begin)* as *const _ as usize, &$root.$($end)* as *const _ as usize)
- };
- (@helper $root:ident, $(# $begin:tt)+ [] ..) => {
- (&$root.$($begin)* as *const _ as usize,
- &$root as *const _ as usize + $crate::mem::size_of_val(&$root))
- };
- (@helper $root:ident, $(# $begin:tt)+ [] ..=) => {
- compile_error!(
+ // Lots of UB due to taking references to uninitialized fields! But that can currently
+ // not be avoided.
+ // No explicit begin for range.
+ (@helper $root:ident, $parent:tt, [] ..) => {{
+ ($root as usize,
+ $root as usize + $crate::mem::size_of_val(&(*$root)))
+ }};
+ (@helper $root:ident, $parent:tt, [] ..= $field:tt) => {{
+ _memoffset__field_check!($parent, $field);
+ ($root as usize,
+ &(*$root).$field as *const _ as usize + $crate::mem::size_of_val(&(*$root).$field))
+ }};
+ (@helper $root:ident, $parent:tt, [] .. $field:tt) => {{
+ _memoffset__field_check!($parent, $field);
+ ($root as usize, &(*$root).$field as *const _ as usize)
+ }};
+ // Explicit begin and end for range.
+ (@helper $root:ident, $parent:tt, # $begin:tt [] ..= $end:tt) => {{
+ _memoffset__field_check!($parent, $begin);
+ _memoffset__field_check!($parent, $end);
+ (&(*$root).$begin as *const _ as usize,
+ &(*$root).$end as *const _ as usize + $crate::mem::size_of_val(&(*$root).$end))
+ }};
+ (@helper $root:ident, $parent:tt, # $begin:tt [] .. $end:tt) => {{
+ _memoffset__field_check!($parent, $begin);
+ _memoffset__field_check!($parent, $end);
+ (&(*$root).$begin as *const _ as usize,
+ &(*$root).$end as *const _ as usize)
+ }};
+ // No explicit end for range.
+ (@helper $root:ident, $parent:tt, # $begin:tt [] ..) => {{
+ _memoffset__field_check!($parent, $begin);
+ (&(*$root).$begin as *const _ as usize,
+ $root as usize + $crate::mem::size_of_val(&*$root))
+ }};
+ (@helper $root:ident, $parent:tt, # $begin:tt [] ..=) => {{
+ _memoffset__compile_error!(
"Found inclusive range to the end of a struct. Did you mean '..' instead of '..='?")
- };
- (@helper $root:ident, $(# $begin:tt)+ []) => {
- (&$root.$($begin)* as *const _ as usize,
- &$root.$($begin)* as *const _ as usize + $crate::mem::size_of_val(&$root.$($begin)*))
- };
- (@helper $root:ident, $(# $begin:tt)+ [] $tt:tt $($rest:tt)*) => {
- span_of!(@helper $root, $(#$begin)* #$tt [] $($rest)*)
- };
- (@helper $root:ident, [] $tt:tt $($rest:tt)*) => {
- span_of!(@helper $root, #$tt [] $($rest)*)
- };
+ }};
+ // Just one field.
+ (@helper $root:ident, $parent:tt, # $begin:tt []) => {{
+ _memoffset__field_check!($parent, $begin);
+ (&(*$root).$begin as *const _ as usize,
+ &(*$root).$begin as *const _ as usize + $crate::mem::size_of_val(&(*$root).$begin))
+ }};
+ // Parsing.
+ (@helper $root:ident, $parent:tt, $(# $begin:tt)+ [] $tt:tt $($rest:tt)*) => {{
+ span_of!(@helper $root, $parent, $(#$begin)* #$tt [] $($rest)*)
+ }};
+ (@helper $root:ident, $parent:tt, [] $tt:tt $($rest:tt)*) => {{
+ span_of!(@helper $root, $parent, #$tt [] $($rest)*)
+ }};
- ($sty:ty, $($exp:tt)+) => ({
- unsafe {
- let root: $sty = $crate::mem::uninitialized();
- let base = &root as *const _ as usize;
- let (begin, end) = span_of!(@helper root, [] $($exp)*);
+ // Entry point.
+ ($sty:tt, $($exp:tt)+) => ({
+ unsafe {
+ // Get a base pointer.
+ _memoffset__let_base_ptr!(root, $sty);
+ let base = root as usize;
+ let (begin, end) = span_of!(@helper root, $sty, [] $($exp)*);
begin-base..end-base
}
});
#[cfg(test)]
mod tests {
- use ::core::mem;
-
- #[repr(C, packed)]
- struct Foo {
- a: u32,
- b: [u8; 4],
- c: i64,
- }
+ use core::mem;
#[test]
fn span_simple() {
+ #[repr(C)]
+ struct Foo {
+ a: u32,
+ b: [u8; 2],
+ c: i64,
+ }
+
assert_eq!(span_of!(Foo, a), 0..4);
- assert_eq!(span_of!(Foo, b), 4..8);
- assert_eq!(span_of!(Foo, c), 8..16);
+ assert_eq!(span_of!(Foo, b), 4..6);
+ assert_eq!(span_of!(Foo, c), 8..8 + 8);
}
#[test]
- fn span_index() {
- assert_eq!(span_of!(Foo, b[1]), 5..6);
+ #[cfg(not(miri))] // this creates unaligned references
+ fn span_simple_packed() {
+ #[repr(C, packed)]
+ struct Foo {
+ a: u32,
+ b: [u8; 2],
+ c: i64,
+ }
+
+ assert_eq!(span_of!(Foo, a), 0..4);
+ assert_eq!(span_of!(Foo, b), 4..6);
+ assert_eq!(span_of!(Foo, c), 6..6 + 8);
}
#[test]
fn span_forms() {
- #[repr(C, packed)]
+ #[repr(C)]
struct Florp {
a: u32,
}
- #[repr(C, packed)]
+ #[repr(C)]
struct Blarg {
x: u64,
y: [u8; 56],
z: Florp,
- egg: [[u8; 4]; 4],
+ egg: [[u8; 4]; 5],
}
// Love me some brute force
assert_eq!(0..8, span_of!(Blarg, x));
- assert_eq!(64..68, span_of!(Blarg, z.a));
- assert_eq!(79..80, span_of!(Blarg, egg[2][3]));
+ assert_eq!(64..68, span_of!(Blarg, z));
+ assert_eq!(68..mem::size_of::<Blarg>(), span_of!(Blarg, egg));
- assert_eq!(8..64, span_of!(Blarg, y[0]..z));
- assert_eq!(0..42, span_of!(Blarg, x..y[34]));
- assert_eq!(0..64, span_of!(Blarg, x ..= y));
- assert_eq!(58..68, span_of!(Blarg, y[50] ..= z));
+ assert_eq!(8..64, span_of!(Blarg, y..z));
+ assert_eq!(0..64, span_of!(Blarg, x..=y));
}
#[test]
assert_eq!(span_of!(Test, ..=x), 0..8);
assert_eq!(span_of!(Test, ..y), 0..8);
assert_eq!(span_of!(Test, ..=y), 0..64);
- assert_eq!(span_of!(Test, ..y[0]), 0..8);
- assert_eq!(span_of!(Test, ..=y[0]), 0..9);
assert_eq!(span_of!(Test, ..z), 0..64);
assert_eq!(span_of!(Test, ..=z), 0..68);
- assert_eq!(span_of!(Test, ..z.foo), 0..64);
- assert_eq!(span_of!(Test, ..=z.foo), 0..68);
assert_eq!(span_of!(Test, ..egg), 0..68);
assert_eq!(span_of!(Test, ..=egg), 0..84);
- assert_eq!(span_of!(Test, ..egg[0]), 0..68);
- assert_eq!(span_of!(Test, ..=egg[0]), 0..72);
- assert_eq!(span_of!(Test, ..egg[0][0]), 0..68);
- assert_eq!(span_of!(Test, ..=egg[0][0]), 0..69);
+ assert_eq!(span_of!(Test, ..), 0..mem::size_of::<Test>());
assert_eq!(
span_of!(Test, x..),
offset_of!(Test, x)..mem::size_of::<Test>()
span_of!(Test, y..),
offset_of!(Test, y)..mem::size_of::<Test>()
);
- assert_eq!(
- span_of!(Test, y[0]..),
- offset_of!(Test, y[0])..mem::size_of::<Test>()
- );
+
assert_eq!(
span_of!(Test, z..),
offset_of!(Test, z)..mem::size_of::<Test>()
);
- assert_eq!(
- span_of!(Test, z.foo..),
- offset_of!(Test, z.foo)..mem::size_of::<Test>()
- );
assert_eq!(
span_of!(Test, egg..),
offset_of!(Test, egg)..mem::size_of::<Test>()
);
- assert_eq!(
- span_of!(Test, egg[0]..),
- offset_of!(Test, egg[0])..mem::size_of::<Test>()
- );
- assert_eq!(
- span_of!(Test, egg[0][0]..),
- offset_of!(Test, egg[0][0])..mem::size_of::<Test>()
- );
assert_eq!(
span_of!(Test, x..y),
offset_of!(Test, x)..offset_of!(Test, y)
span_of!(Test, x..=y),
offset_of!(Test, x)..offset_of!(Test, y) + mem::size_of::<[u8; 56]>()
);
- assert_eq!(
- span_of!(Test, x..y[4]),
- offset_of!(Test, x)..offset_of!(Test, y[4])
- );
- assert_eq!(
- span_of!(Test, x..=y[4]),
- offset_of!(Test, x)..offset_of!(Test, y) + mem::size_of::<[u8; 5]>()
- );
- assert_eq!(
- span_of!(Test, x..z.foo),
- offset_of!(Test, x)..offset_of!(Test, z.foo)
- );
- assert_eq!(
- span_of!(Test, x..=z.foo),
- offset_of!(Test, x)..offset_of!(Test, z.foo) + mem::size_of::<u32>()
- );
- assert_eq!(
- span_of!(Test, egg[0][0]..egg[1][0]),
- offset_of!(Test, egg[0][0])..offset_of!(Test, egg[1][0])
- );
}
}