memoffset/
span_of.rs

1// Copyright (c) 2017 Gilad Naaman
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21/// Reexport for `local_inner_macros`; see
22/// <https://doc.rust-lang.org/edition-guide/rust-2018/macros/macro-changes.html#macros-using-local_inner_macros>.
23#[doc(hidden)]
24#[macro_export]
25macro_rules! _memoffset__compile_error {
26    ($($inner:tt)*) => {
27        compile_error! { $($inner)* }
28    }
29}
30
31/// Produces a range instance representing the sub-slice containing the specified member.
32///
33/// This macro provides 2 forms of differing functionalities.
34///
35/// The first form is identical to the appearance of the `offset_of!` macro.
36///
37/// ```ignore
38/// span_of!(Struct, member)
39/// ```
40///
41/// The second form of `span_of!` returns a sub-slice which starts at one field, and ends at another.
42/// The general pattern of this form is:
43///
44/// ```ignore
45/// // Exclusive
46/// span_of!(Struct, member_a .. member_b)
47/// // Inclusive
48/// span_of!(Struct, member_a ..= member_b)
49///
50/// // Open-ended ranges
51/// span_of!(Struct, .. end)
52/// span_of!(Struct, start ..)
53/// ```
54///
55/// *Note*:
56/// This macro uses recursion in order to resolve the range expressions, so there is a limit to
57/// the complexity of the expression.
58/// In order to raise the limit, the compiler's recursion limit should be lifted.
59///
60/// ## Examples
61/// ```
62/// use memoffset::span_of;
63///
64/// #[repr(C)]
65/// struct Florp {
66///     a: u32
67/// }
68///
69/// #[repr(C)]
70/// struct Blarg {
71///     x: [u32; 2],
72///     y: [u8; 56],
73///     z: Florp,
74///     egg: [[u8; 4]; 4]
75/// }
76///
77/// fn main() {
78///     assert_eq!(0..84,  span_of!(Blarg, ..));
79///     assert_eq!(0..8,   span_of!(Blarg, .. y));
80///     assert_eq!(0..64,  span_of!(Blarg, ..= y));
81///     assert_eq!(0..8,   span_of!(Blarg, x));
82///     assert_eq!(8..84,  span_of!(Blarg, y ..));
83///     assert_eq!(0..8,   span_of!(Blarg, x .. y));
84///     assert_eq!(0..64,  span_of!(Blarg, x ..= y));
85/// }
86/// ```
87#[macro_export(local_inner_macros)]
88macro_rules! span_of {
89    (@helper  $root:ident, [] ..=) => {
90        _memoffset__compile_error!("Expected a range, found '..='")
91    };
92    (@helper $root:ident, [] ..) => {
93        _memoffset__compile_error!("Expected a range, found '..'")
94    };
95    // No explicit begin for range.
96    (@helper $root:ident, $parent:path, [] ..) => {{
97        ($root as usize,
98         $root as usize + $crate::__priv::size_of_pointee($root))
99    }};
100    (@helper $root:ident, $parent:path, [] ..= $end:tt) => {{
101        let end = raw_field!($root, $parent, $end);
102        ($root as usize, end as usize + $crate::__priv::size_of_pointee(end))
103    }};
104    (@helper $root:ident, $parent:path, [] .. $end:tt) => {{
105        ($root as usize, raw_field!($root, $parent, $end) as usize)
106    }};
107    // Explicit begin and end for range.
108    (@helper $root:ident, $parent:path, # $begin:tt [] ..= $end:tt) => {{
109        let begin = raw_field!($root, $parent, $begin);
110        let end = raw_field!($root, $parent, $end);
111        (begin as usize, end as usize + $crate::__priv::size_of_pointee(end))
112    }};
113    (@helper $root:ident, $parent:path, # $begin:tt [] .. $end:tt) => {{
114        (raw_field!($root, $parent, $begin) as usize,
115         raw_field!($root, $parent, $end) as usize)
116    }};
117    // No explicit end for range.
118    (@helper $root:ident, $parent:path, # $begin:tt [] ..) => {{
119        (raw_field!($root, $parent, $begin) as usize,
120         $root as usize + $crate::__priv::size_of_pointee($root))
121    }};
122    (@helper $root:ident, $parent:path, # $begin:tt [] ..=) => {{
123        _memoffset__compile_error!(
124            "Found inclusive range to the end of a struct. Did you mean '..' instead of '..='?")
125    }};
126    // Just one field.
127    (@helper $root:ident, $parent:path, # $field:tt []) => {{
128        let field = raw_field!($root, $parent, $field);
129        (field as usize, field as usize + $crate::__priv::size_of_pointee(field))
130    }};
131    // Parsing.
132    (@helper $root:ident, $parent:path, $(# $begin:tt)+ [] $tt:tt $($rest:tt)*) => {{
133        span_of!(@helper $root, $parent, $(#$begin)* #$tt [] $($rest)*)
134    }};
135    (@helper $root:ident, $parent:path, [] $tt:tt $($rest:tt)*) => {{
136        span_of!(@helper $root, $parent, #$tt [] $($rest)*)
137    }};
138
139    // Entry point.
140    ($sty:path, $($exp:tt)+) => ({
141        // Get a base pointer.
142        _memoffset__let_base_ptr!(root, $sty);
143        let base = root as usize;
144        let (begin, end) = span_of!(@helper root, $sty, [] $($exp)*);
145        begin-base..end-base
146    });
147}
148
149#[cfg(test)]
150mod tests {
151    use core::mem;
152
153    #[test]
154    fn span_simple() {
155        #[repr(C)]
156        struct Foo {
157            a: u32,
158            b: [u8; 2],
159            c: i64,
160        }
161
162        assert_eq!(span_of!(Foo, a), 0..4);
163        assert_eq!(span_of!(Foo, b), 4..6);
164        assert_eq!(span_of!(Foo, c), 8..8 + 8);
165    }
166
167    #[test]
168    #[cfg_attr(miri, ignore)] // this creates unaligned references
169    fn span_simple_packed() {
170        #[repr(C, packed)]
171        struct Foo {
172            a: u32,
173            b: [u8; 2],
174            c: i64,
175        }
176
177        assert_eq!(span_of!(Foo, a), 0..4);
178        assert_eq!(span_of!(Foo, b), 4..6);
179        assert_eq!(span_of!(Foo, c), 6..6 + 8);
180    }
181
182    #[test]
183    fn span_forms() {
184        #[repr(C)]
185        struct Florp {
186            a: u32,
187        }
188
189        #[repr(C)]
190        struct Blarg {
191            x: u64,
192            y: [u8; 56],
193            z: Florp,
194            egg: [[u8; 4]; 5],
195        }
196
197        // Love me some brute force
198        assert_eq!(0..8, span_of!(Blarg, x));
199        assert_eq!(64..68, span_of!(Blarg, z));
200        assert_eq!(68..mem::size_of::<Blarg>(), span_of!(Blarg, egg));
201
202        assert_eq!(8..64, span_of!(Blarg, y..z));
203        assert_eq!(0..64, span_of!(Blarg, x..=y));
204    }
205
206    #[test]
207    fn ig_test() {
208        #[repr(C)]
209        struct Member {
210            foo: u32,
211        }
212
213        #[repr(C)]
214        struct Test {
215            x: u64,
216            y: [u8; 56],
217            z: Member,
218            egg: [[u8; 4]; 4],
219        }
220
221        assert_eq!(span_of!(Test, ..x), 0..0);
222        assert_eq!(span_of!(Test, ..=x), 0..8);
223        assert_eq!(span_of!(Test, ..y), 0..8);
224        assert_eq!(span_of!(Test, ..=y), 0..64);
225        assert_eq!(span_of!(Test, ..z), 0..64);
226        assert_eq!(span_of!(Test, ..=z), 0..68);
227        assert_eq!(span_of!(Test, ..egg), 0..68);
228        assert_eq!(span_of!(Test, ..=egg), 0..84);
229        assert_eq!(span_of!(Test, ..), 0..mem::size_of::<Test>());
230        assert_eq!(
231            span_of!(Test, x..),
232            offset_of!(Test, x)..mem::size_of::<Test>()
233        );
234        assert_eq!(
235            span_of!(Test, y..),
236            offset_of!(Test, y)..mem::size_of::<Test>()
237        );
238
239        assert_eq!(
240            span_of!(Test, z..),
241            offset_of!(Test, z)..mem::size_of::<Test>()
242        );
243        assert_eq!(
244            span_of!(Test, egg..),
245            offset_of!(Test, egg)..mem::size_of::<Test>()
246        );
247        assert_eq!(
248            span_of!(Test, x..y),
249            offset_of!(Test, x)..offset_of!(Test, y)
250        );
251        assert_eq!(
252            span_of!(Test, x..=y),
253            offset_of!(Test, x)..offset_of!(Test, y) + mem::size_of::<[u8; 56]>()
254        );
255    }
256}