aboutsummaryrefslogtreecommitdiff
path: root/rust/qemu-api/src/offset_of.rs
blob: 075e98f986ba9bbc9cb4c16b2d38a2d751a54f18 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
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);
    }
}