|
@@ -0,0 +1,161 @@
|
|
|
+// SPDX-License-Identifier: MIT
|
|
|
+
|
|
|
+/// This macro provides the same functionality as `core::mem::offset_of`,
|
|
|
+/// except that only one level of field access is supported. The declaration
|
|
|
+/// of the struct must be wrapped with `with_offsets! { }`.
|
|
|
+///
|
|
|
+/// It is needed because `offset_of!` was only stabilized in Rust 1.77.
|
|
|
+#[cfg(not(has_offset_of))]
|
|
|
+#[macro_export]
|
|
|
+macro_rules! offset_of {
|
|
|
+ ($Container:ty, $field:ident) => {
|
|
|
+ <$Container>::OFFSET_TO__.$field
|
|
|
+ };
|
|
|
+}
|
|
|
+
|
|
|
+/// A wrapper for struct declarations, that allows using `offset_of!` in
|
|
|
+/// versions of Rust prior to 1.77
|
|
|
+#[macro_export]
|
|
|
+macro_rules! with_offsets {
|
|
|
+ // This method to generate field offset constants comes from:
|
|
|
+ //
|
|
|
+ // https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=10a22a9b8393abd7b541d8fc844bc0df
|
|
|
+ //
|
|
|
+ // used under MIT license with permission of Yandros aka Daniel Henry-Mantilla
|
|
|
+ (
|
|
|
+ $(#[$struct_meta:meta])*
|
|
|
+ $struct_vis:vis
|
|
|
+ struct $StructName:ident {
|
|
|
+ $(
|
|
|
+ $(#[$field_meta:meta])*
|
|
|
+ $field_vis:vis
|
|
|
+ $field_name:ident : $field_ty:ty
|
|
|
+ ),*
|
|
|
+ $(,)?
|
|
|
+ }
|
|
|
+ ) => (
|
|
|
+ #[cfg(not(has_offset_of))]
|
|
|
+ const _: () = {
|
|
|
+ struct StructOffsetsHelper<T>(std::marker::PhantomData<T>);
|
|
|
+ const END_OF_PREV_FIELD: usize = 0;
|
|
|
+
|
|
|
+ // populate StructOffsetsHelper<T> with associated consts,
|
|
|
+ // one for each field
|
|
|
+ $crate::with_offsets! {
|
|
|
+ @struct $StructName
|
|
|
+ @names [ $($field_name)* ]
|
|
|
+ @tys [ $($field_ty ,)*]
|
|
|
+ }
|
|
|
+
|
|
|
+ // now turn StructOffsetsHelper<T>'s consts into a single struct,
|
|
|
+ // applying field visibility. This provides better error messages
|
|
|
+ // than if offset_of! used StructOffsetsHelper::<T> directly.
|
|
|
+ pub
|
|
|
+ struct StructOffsets {
|
|
|
+ $(
|
|
|
+ $field_vis
|
|
|
+ $field_name: usize,
|
|
|
+ )*
|
|
|
+ }
|
|
|
+ impl $StructName {
|
|
|
+ pub
|
|
|
+ const OFFSET_TO__: StructOffsets = StructOffsets {
|
|
|
+ $(
|
|
|
+ $field_name: StructOffsetsHelper::<$StructName>::$field_name,
|
|
|
+ )*
|
|
|
+ };
|
|
|
+ }
|
|
|
+ };
|
|
|
+ );
|
|
|
+
|
|
|
+ (
|
|
|
+ @struct $StructName:ident
|
|
|
+ @names []
|
|
|
+ @tys []
|
|
|
+ ) => ();
|
|
|
+
|
|
|
+ (
|
|
|
+ @struct $StructName:ident
|
|
|
+ @names [$field_name:ident $($other_names:tt)*]
|
|
|
+ @tys [$field_ty:ty , $($other_tys:tt)*]
|
|
|
+ ) => (
|
|
|
+ #[allow(non_local_definitions)]
|
|
|
+ #[allow(clippy::modulo_one)]
|
|
|
+ impl StructOffsetsHelper<$StructName> {
|
|
|
+ #[allow(nonstandard_style)]
|
|
|
+ const $field_name: usize = {
|
|
|
+ const ALIGN: usize = std::mem::align_of::<$field_ty>();
|
|
|
+ const TRAIL: usize = END_OF_PREV_FIELD % ALIGN;
|
|
|
+ END_OF_PREV_FIELD + (if TRAIL == 0 { 0usize } else { ALIGN - TRAIL })
|
|
|
+ };
|
|
|
+ }
|
|
|
+ const _: () = {
|
|
|
+ const END_OF_PREV_FIELD: usize =
|
|
|
+ StructOffsetsHelper::<$StructName>::$field_name +
|
|
|
+ std::mem::size_of::<$field_ty>()
|
|
|
+ ;
|
|
|
+ $crate::with_offsets! {
|
|
|
+ @struct $StructName
|
|
|
+ @names [$($other_names)*]
|
|
|
+ @tys [$($other_tys)*]
|
|
|
+ }
|
|
|
+ };
|
|
|
+ );
|
|
|
+}
|
|
|
+
|
|
|
+#[cfg(test)]
|
|
|
+mod tests {
|
|
|
+ use crate::offset_of;
|
|
|
+
|
|
|
+ #[repr(C)]
|
|
|
+ struct Foo {
|
|
|
+ a: u16,
|
|
|
+ b: u32,
|
|
|
+ c: u64,
|
|
|
+ d: u16,
|
|
|
+ }
|
|
|
+
|
|
|
+ #[repr(C)]
|
|
|
+ struct Bar {
|
|
|
+ pub a: u16,
|
|
|
+ pub b: u64,
|
|
|
+ c: Foo,
|
|
|
+ d: u64,
|
|
|
+ }
|
|
|
+
|
|
|
+ crate::with_offsets! {
|
|
|
+ #[repr(C)]
|
|
|
+ struct Bar {
|
|
|
+ pub a: u16,
|
|
|
+ pub b: u64,
|
|
|
+ c: Foo,
|
|
|
+ d: u64,
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ #[repr(C)]
|
|
|
+ pub struct Baz {
|
|
|
+ b: u32,
|
|
|
+ a: u8,
|
|
|
+ }
|
|
|
+ crate::with_offsets! {
|
|
|
+ #[repr(C)]
|
|
|
+ pub struct Baz {
|
|
|
+ b: u32,
|
|
|
+ a: u8,
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn test_offset_of() {
|
|
|
+ const OFFSET_TO_C: usize = offset_of!(Bar, c);
|
|
|
+
|
|
|
+ assert_eq!(offset_of!(Bar, a), 0);
|
|
|
+ assert_eq!(offset_of!(Bar, b), 8);
|
|
|
+ assert_eq!(OFFSET_TO_C, 16);
|
|
|
+ assert_eq!(offset_of!(Bar, d), 40);
|
|
|
+
|
|
|
+ assert_eq!(offset_of!(Baz, b), 0);
|
|
|
+ assert_eq!(offset_of!(Baz, a), 4);
|
|
|
+ }
|
|
|
+}
|