Support tuples in offset_of (#48)

Support tuple types via new offset_of_tuple / raw_field_tuple macros
This commit is contained in:
Andrew Gaspar
2020-10-23 01:16:25 -06:00
committed by GitHub
parent 4832512e36
commit 7dc779f328
4 changed files with 96 additions and 3 deletions
+1
View File
@@ -6,6 +6,7 @@ C-Like `offset_of` functionality for Rust structs.
Introduces the following macros:
* `offset_of!` for obtaining the offset of a member of a struct.
* `offset_of_tuple!` for obtaining the offset of a member of a tuple. (Requires Rust 1.20+)
* `span_of!` for obtaining the range that a field, or fields, span.
`memoffset` works under `no_std` environments.
+3
View File
@@ -4,6 +4,9 @@ fn main() {
let ac = autocfg::new();
// Check for a minimum version for a few features
if ac.probe_rustc_version(1, 20) {
println!("cargo:rustc-cfg=tuple_ty");
}
if ac.probe_rustc_version(1, 31) {
println!("cargo:rustc-cfg=allow_clippy");
}
+61 -3
View File
@@ -24,7 +24,7 @@
#[macro_export]
#[doc(hidden)]
macro_rules! _memoffset__let_base_ptr {
($name:ident, $type:path) => {
($name:ident, $type:ty) => {
// No UB here, and the pointer does not dangle, either.
// But we have to make sure that `uninit` lives long enough,
// so it has to be in the same scope as `$name`. That's why
@@ -38,7 +38,7 @@ macro_rules! _memoffset__let_base_ptr {
#[macro_export]
#[doc(hidden)]
macro_rules! _memoffset__let_base_ptr {
($name:ident, $type:path) => {
($name:ident, $type:ty) => {
// No UB right here, but we will later dereference this pointer to
// offset into a field, and that is UB because the pointer is dangling.
let $name = $crate::mem::align_of::<$type>() as *const $type;
@@ -66,7 +66,7 @@ macro_rules! _memoffset_offset_from {
};
}
/// Calculates the offset of the specified field from the start of the struct.
/// Calculates the offset of the specified field from the start of the named struct.
///
/// ## Examples
/// ```
@@ -97,6 +97,30 @@ macro_rules! offset_of {
}};
}
/// Calculates the offset of the specified field from the start of the tuple.
///
/// ## Examples
/// ```
/// #[macro_use]
/// extern crate memoffset;
///
/// fn main() {
/// assert!(offset_of_tuple!((u8, u32), 1) >= 0, "Tuples do not have a defined layout");
/// }
/// ```
#[cfg(tuple_ty)]
#[macro_export(local_inner_macros)]
macro_rules! offset_of_tuple {
($parent:ty, $field:tt) => {{
// Get a base pointer (non-dangling if rustc supports `MaybeUninit`).
_memoffset__let_base_ptr!(base_ptr, $parent);
// Get field pointer.
let field_ptr = raw_field_tuple!(base_ptr, $parent, $field);
// Compute offset.
_memoffset_offset_from!(field_ptr, base_ptr)
}};
}
#[cfg(test)]
mod tests {
#[test]
@@ -160,6 +184,19 @@ mod tests {
assert_eq!(foo(Pair(0, 0)), 4);
}
#[cfg(tuple_ty)]
#[test]
fn test_tuple_offset() {
let f = (0i32, 0.0f32, 0u8);
let f_ptr = &f as *const _;
let f1_ptr = &f.1 as *const _;
assert_eq!(
f1_ptr as usize - f_ptr as usize,
offset_of_tuple!((i32, f32, u8), 1)
);
}
#[test]
fn test_raw_field() {
#[repr(C)]
@@ -180,6 +217,27 @@ mod tests {
assert_eq!(f_ptr as usize + 8, raw_field!(f_ptr, Foo, c) as usize);
}
#[cfg(tuple_ty)]
#[test]
fn test_raw_field_tuple() {
let t = (0u32, 0u8, false);
let t_ptr = &t as *const _;
let t_addr = t_ptr as usize;
assert_eq!(
&t.0 as *const _ as usize - t_addr,
raw_field_tuple!(t_ptr, (u32, u8, bool), 0) as usize - t_addr
);
assert_eq!(
&t.1 as *const _ as usize - t_addr,
raw_field_tuple!(t_ptr, (u32, u8, bool), 1) as usize - t_addr
);
assert_eq!(
&t.2 as *const _ as usize - t_addr,
raw_field_tuple!(t_ptr, (u32, u8, bool), 2) as usize - t_addr
);
}
#[cfg(feature = "unstable_const")]
#[test]
fn const_offset() {
+31
View File
@@ -63,6 +63,16 @@ macro_rules! _memoffset__field_check {
};
}
/// Deref-coercion protection macro.
#[macro_export]
#[doc(hidden)]
macro_rules! _memoffset__field_check_tuple {
($type:ty, $field:tt) => {
// Make sure the type argument is a tuple
let (_, ..): $type;
};
}
/// Computes a const raw pointer to the given field of the given base pointer
/// to the given parent type.
///
@@ -82,3 +92,24 @@ macro_rules! raw_field {
}
}};
}
/// Computes a const raw pointer to the given field of the given base pointer
/// to the given parent tuple typle.
///
/// The `base` pointer *must not* be dangling, but it *may* point to
/// uninitialized memory.
#[cfg(tuple_ty)]
#[macro_export(local_inner_macros)]
macro_rules! raw_field_tuple {
($base:expr, $parent:ty, $field:tt) => {{
_memoffset__field_check_tuple!($parent, $field);
// Get the field address.
// Crucially, we know that this will not trigger a deref coercion because
// of the field check we did above.
#[allow(unused_unsafe)] // for when the macro is used in an unsafe block
unsafe {
_memoffset__raw_const!((*($base as *const $parent)).$field)
}
}};
}