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 |