mirror of
https://github.com/openharmony/third_party_rust_memoffset.git
synced 2026-07-18 12:25:36 -04:00
Support tuples in offset_of (#48)
Support tuple types via new offset_of_tuple / raw_field_tuple macros
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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
@@ -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() {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user