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 ] |
25 | macro_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 | /// ### Safety |
61 | /// The inter-field form mentioned above assumes that the first field is positioned before the |
62 | /// second. |
63 | /// This is only guarenteed for `repr(C)` structs. |
64 | /// Usage with `repr(Rust)` structs may yield unexpected results, like downward-going ranges, |
65 | /// spans that include unexpected fields, empty spans, or spans that include *unexpected* padding bytes. |
66 | /// |
67 | /// ## Examples |
68 | /// ``` |
69 | /// use memoffset::span_of; |
70 | /// |
71 | /// #[repr(C)] |
72 | /// struct Florp { |
73 | /// a: u32 |
74 | /// } |
75 | /// |
76 | /// #[repr(C)] |
77 | /// struct Blarg { |
78 | /// x: [u32; 2], |
79 | /// y: [u8; 56], |
80 | /// z: Florp, |
81 | /// egg: [[u8; 4]; 4] |
82 | /// } |
83 | /// |
84 | /// fn main() { |
85 | /// assert_eq!(0..84, span_of!(Blarg, ..)); |
86 | /// assert_eq!(0..8, span_of!(Blarg, .. y)); |
87 | /// assert_eq!(0..64, span_of!(Blarg, ..= y)); |
88 | /// assert_eq!(0..8, span_of!(Blarg, x)); |
89 | /// assert_eq!(8..84, span_of!(Blarg, y ..)); |
90 | /// assert_eq!(0..8, span_of!(Blarg, x .. y)); |
91 | /// assert_eq!(0..64, span_of!(Blarg, x ..= y)); |
92 | /// } |
93 | /// ``` |
94 | #[macro_export (local_inner_macros)] |
95 | macro_rules! span_of { |
96 | (@helper $root:ident, [] ..=) => { |
97 | _memoffset__compile_error!("Expected a range, found '..='" ) |
98 | }; |
99 | (@helper $root:ident, [] ..) => { |
100 | _memoffset__compile_error!("Expected a range, found '..'" ) |
101 | }; |
102 | // No explicit begin for range. |
103 | (@helper $root:ident, $parent:path, [] ..) => {{ |
104 | ($root as usize, |
105 | $root as usize + $crate::__priv::size_of_pointee($root)) |
106 | }}; |
107 | (@helper $root:ident, $parent:path, [] ..= $end:tt) => {{ |
108 | let end = raw_field!($root, $parent, $end); |
109 | ($root as usize, end as usize + $crate::__priv::size_of_pointee(end)) |
110 | }}; |
111 | (@helper $root:ident, $parent:path, [] .. $end:tt) => {{ |
112 | ($root as usize, raw_field!($root, $parent, $end) as usize) |
113 | }}; |
114 | // Explicit begin and end for range. |
115 | (@helper $root:ident, $parent:path, # $begin:tt [] ..= $end:tt) => {{ |
116 | let begin = raw_field!($root, $parent, $begin); |
117 | let end = raw_field!($root, $parent, $end); |
118 | (begin as usize, end as usize + $crate::__priv::size_of_pointee(end)) |
119 | }}; |
120 | (@helper $root:ident, $parent:path, # $begin:tt [] .. $end:tt) => {{ |
121 | (raw_field!($root, $parent, $begin) as usize, |
122 | raw_field!($root, $parent, $end) as usize) |
123 | }}; |
124 | // No explicit end for range. |
125 | (@helper $root:ident, $parent:path, # $begin:tt [] ..) => {{ |
126 | (raw_field!($root, $parent, $begin) as usize, |
127 | $root as usize + $crate::__priv::size_of_pointee($root)) |
128 | }}; |
129 | (@helper $root:ident, $parent:path, # $begin:tt [] ..=) => {{ |
130 | _memoffset__compile_error!( |
131 | "Found inclusive range to the end of a struct. Did you mean '..' instead of '..='?" ) |
132 | }}; |
133 | // Just one field. |
134 | (@helper $root:ident, $parent:path, # $field:tt []) => {{ |
135 | let field = raw_field!($root, $parent, $field); |
136 | (field as usize, field as usize + $crate::__priv::size_of_pointee(field)) |
137 | }}; |
138 | // Parsing. |
139 | (@helper $root:ident, $parent:path, $(# $begin:tt)+ [] $tt:tt $($rest:tt)*) => {{ |
140 | span_of!(@helper $root, $parent, $(#$begin)* #$tt [] $($rest)*) |
141 | }}; |
142 | (@helper $root:ident, $parent:path, [] $tt:tt $($rest:tt)*) => {{ |
143 | span_of!(@helper $root, $parent, #$tt [] $($rest)*) |
144 | }}; |
145 | |
146 | // Entry point. |
147 | ($sty:path, $($exp:tt)+) => ({ |
148 | // Get a base pointer. |
149 | _memoffset__let_base_ptr!(root, $sty); |
150 | let base = root as usize; |
151 | let (begin, end) = span_of!(@helper root, $sty, [] $($exp)*); |
152 | begin-base..end-base |
153 | }); |
154 | } |
155 | |
156 | #[cfg (test)] |
157 | mod tests { |
158 | use core::mem; |
159 | |
160 | #[test ] |
161 | fn span_simple() { |
162 | #[repr (C)] |
163 | struct Foo { |
164 | a: u32, |
165 | b: [u8; 2], |
166 | c: i64, |
167 | } |
168 | |
169 | assert_eq!(span_of!(Foo, a), 0..4); |
170 | assert_eq!(span_of!(Foo, b), 4..6); |
171 | assert_eq!(span_of!(Foo, c), 8..8 + 8); |
172 | } |
173 | |
174 | #[test ] |
175 | #[cfg_attr (miri, ignore)] // this creates unaligned references |
176 | fn span_simple_packed() { |
177 | #[repr (C, packed)] |
178 | struct Foo { |
179 | a: u32, |
180 | b: [u8; 2], |
181 | c: i64, |
182 | } |
183 | |
184 | assert_eq!(span_of!(Foo, a), 0..4); |
185 | assert_eq!(span_of!(Foo, b), 4..6); |
186 | assert_eq!(span_of!(Foo, c), 6..6 + 8); |
187 | } |
188 | |
189 | #[test ] |
190 | fn span_forms() { |
191 | #[repr (C)] |
192 | struct Florp { |
193 | a: u32, |
194 | } |
195 | |
196 | #[repr (C)] |
197 | struct Blarg { |
198 | x: u64, |
199 | y: [u8; 56], |
200 | z: Florp, |
201 | egg: [[u8; 4]; 5], |
202 | } |
203 | |
204 | // Love me some brute force |
205 | assert_eq!(0..8, span_of!(Blarg, x)); |
206 | assert_eq!(64..68, span_of!(Blarg, z)); |
207 | assert_eq!(68..mem::size_of::<Blarg>(), span_of!(Blarg, egg)); |
208 | |
209 | assert_eq!(8..64, span_of!(Blarg, y..z)); |
210 | assert_eq!(0..64, span_of!(Blarg, x..=y)); |
211 | } |
212 | |
213 | #[test ] |
214 | fn ig_test() { |
215 | #[repr (C)] |
216 | struct Member { |
217 | foo: u32, |
218 | } |
219 | |
220 | #[repr (C)] |
221 | struct Test { |
222 | x: u64, |
223 | y: [u8; 56], |
224 | z: Member, |
225 | egg: [[u8; 4]; 4], |
226 | } |
227 | |
228 | assert_eq!(span_of!(Test, ..x), 0..0); |
229 | assert_eq!(span_of!(Test, ..=x), 0..8); |
230 | assert_eq!(span_of!(Test, ..y), 0..8); |
231 | assert_eq!(span_of!(Test, ..=y), 0..64); |
232 | assert_eq!(span_of!(Test, ..z), 0..64); |
233 | assert_eq!(span_of!(Test, ..=z), 0..68); |
234 | assert_eq!(span_of!(Test, ..egg), 0..68); |
235 | assert_eq!(span_of!(Test, ..=egg), 0..84); |
236 | assert_eq!(span_of!(Test, ..), 0..mem::size_of::<Test>()); |
237 | assert_eq!( |
238 | span_of!(Test, x..), |
239 | offset_of!(Test, x)..mem::size_of::<Test>() |
240 | ); |
241 | assert_eq!( |
242 | span_of!(Test, y..), |
243 | offset_of!(Test, y)..mem::size_of::<Test>() |
244 | ); |
245 | |
246 | assert_eq!( |
247 | span_of!(Test, z..), |
248 | offset_of!(Test, z)..mem::size_of::<Test>() |
249 | ); |
250 | assert_eq!( |
251 | span_of!(Test, egg..), |
252 | offset_of!(Test, egg)..mem::size_of::<Test>() |
253 | ); |
254 | assert_eq!( |
255 | span_of!(Test, x..y), |
256 | offset_of!(Test, x)..offset_of!(Test, y) |
257 | ); |
258 | assert_eq!( |
259 | span_of!(Test, x..=y), |
260 | offset_of!(Test, x)..offset_of!(Test, y) + mem::size_of::<[u8; 56]>() |
261 | ); |
262 | } |
263 | } |
264 | |