├── .gitignore ├── Cargo.toml ├── license.txt ├── readme.md └── src └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | # Rust 2 | target/ 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "struct_layout" 3 | version = "0.1.0" 4 | edition = "2018" 5 | 6 | authors = ["Casper "] 7 | description = "Customize your struct layout with this one weird trick." 8 | documentation = "https://docs.rs/struct_layout" 9 | repository = "https://github.com/CasualX/struct_layout" 10 | readme = "readme.md" 11 | license = "MIT" 12 | 13 | keywords = ["explicit", "struct", "layout"] 14 | categories = [] 15 | 16 | [lib] 17 | proc-macro = true 18 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 Casper 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | Struct layout 2 | ============= 3 | 4 | [![MIT License](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 5 | [![crates.io](https://img.shields.io/crates/v/struct_layout.svg)](https://crates.io/crates/struct_layout) 6 | [![docs.rs](https://docs.rs/struct_layout/badge.svg)](https://docs.rs/struct_layout) 7 | 8 | Customize your struct layout with explicit control over the fields. 9 | 10 | Usage 11 | ----- 12 | 13 | This proc-macro is available on [crates.io](https://crates.io/crates/struct_layout). 14 | 15 | Documentation can be found on [docs.rs](https://docs.rs/struct_layout/). 16 | 17 | In your Cargo.toml, put 18 | 19 | ``` 20 | [dependencies] 21 | struct_layout = "0.1" 22 | ``` 23 | 24 | Examples 25 | -------- 26 | 27 | Apply the `#[struct_layout::explicit]` attribute to a struct definition and put `#[field]` attributes on every field declaration. 28 | 29 | The syntax takes inspiration from [C# `[StructLayout]` attribute](https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.structlayoutattribute#examples). 30 | 31 | ```rust 32 | /// Doc comments are allowed. 33 | #[struct_layout::explicit(size = 32, align = 4)] 34 | #[derive(Copy, Clone, Debug, Default)] 35 | pub struct Foo { 36 | #[field(offset = 21, get, set)] 37 | pub unaligned: f32, 38 | 39 | /// Documenting the fields works too. 40 | #[field(offset = 4)] 41 | pub int: i32, 42 | } 43 | ``` 44 | 45 | There's a lot to unpack here, let's go through it step by step. 46 | 47 | ### The struct_layout::explicit attribute 48 | 49 | This attribute must be applied to a struct definition, the attribute arguments order is fixed: 50 | 51 | The size and alignment of the structure are required and follow the format `size = ` and `align = `. 52 | 53 | Following is an optional `check(..)` argument which specifies a trait bound which all field members must implement. 54 | This allows a custom trait to guarantee that all field types are safe to be used. If absent all fields are required to implement `Copy`. 55 | 56 | ### Additional attributes 57 | 58 | Because of the invasive nature of the transformation, only a small set of whitelisted attributes are supported: 59 | 60 | * `doc` comments are allowed and applied to the final generated structure. 61 | * `derive` with a set of whitelisted identifiers. 62 | 63 | ### The generated structure 64 | 65 | The proc-macro generates a newtype tuple structure wrapping a byte array with length specified by the `size` argument. 66 | The structure is adorned by the `C` and `align` representation with the alignment specified by the `align` argument. 67 | The visibility specified is applied to the generated structure. 68 | Any doc comments are also added. 69 | 70 | Basically, this: 71 | 72 | ```rust 73 | /// Doc comments are allowed. 74 | #[repr(C, align(4))] 75 | pub struct Foo([u8; 32]); 76 | ``` 77 | 78 | ### Supported auto derived traits 79 | 80 | The only supported traits to be auto derived are `Copy`, `Clone`, `Debug` and `Default`. 81 | Future extensions may allow more traits to be supported. 82 | 83 | Don't forget you can implement additional methods and traits on the generated type! 84 | 85 | ### Structure field syntax 86 | 87 | Every field in the structure must be accompanied by a single `#[field(..)]` attribute. No other attributes except `doc` comments are allowed. 88 | 89 | The field attribute must start with specifying the offset of the field using `offset = `. 90 | Followed by a list of methods for how to implement access to the field. 91 | 92 | Supported methods are `get`, `set`, `ref` or `mut`. If no methods are specified, they will all be implemented for this field. 93 | The accessor methods have where clause requiring the field type to implement the trait specified by the `check` argument of the `struct_layout::explicit` attribute. 94 | 95 | The accessors are implemented via methods with the following signatures: 96 | 97 | * get: `fn field(&self) -> T` 98 | * set: `fn set_field(&mut self, value: T) -> &mut Self` 99 | * ref: `fn field_ref(&self) -> &T` 100 | * mut: `fn field_mut(&mut self) -> &mut T` 101 | 102 | Get and set allow unaligned offsets. If any of the fields have ref or mut accessors then the field offset and the structure specified alignment must be a multiple of the field type's alignment. 103 | 104 | If the field offset is out of bounds or unaligned (where required) an incomprehensible error is generated complaining about const evaluation failing. 105 | This is unfortunate due to how these constraints are statically asserted (using the array size trick). 106 | 107 | ### Safety 108 | 109 | All fields are required to implement `Copy` or the trait bound specified by the `check` argument. 110 | This restriction makes things safer, but by no means perfect. By using this library you commit to not doing stupid things and perhaps drop an issue in the bugtracker where safety can be improved. 111 | 112 | Under no circumstance should a field be allowed to implement `Drop` or be a reference type. It is not supported. 113 | 114 | ### How to construct an instance 115 | 116 | If requested the `Default` trait may be auto derived filling in the fields with their type's default value. 117 | You may add additional associated methods to the generated structure. 118 | 119 | It is also possible to use the unsafe `std::mem::zeroed` to create a zero initialized instance if this makes sense. 120 | 121 | ### Compatibility with no_std 122 | 123 | The generated code is compatible with `no_std`! 124 | 125 | License 126 | ------- 127 | 128 | Licensed under [MIT License](https://opensource.org/licenses/MIT), see [license.txt](license.txt). 129 | 130 | ### Contribution 131 | 132 | Unless you explicitly state otherwise, any contribution intentionally submitted 133 | for inclusion in the work by you, shall be licensed as above, without any additional terms or conditions. 134 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | Learn by example, for a detailed description see the readme. 3 | 4 | # Examples 5 | 6 | Most simple example, specify the required size and alignment and annotate the fields with an offset. 7 | Use `zeroed` to create an instance and use the generated accessors to access the field. 8 | 9 | ``` 10 | #[struct_layout::explicit(size = 16, align = 4)] 11 | struct Foo { 12 | #[field(offset = 4)] 13 | field: i32, 14 | } 15 | 16 | let mut foo: Foo = unsafe { std::mem::zeroed() }; 17 | 18 | foo.set_field(13); 19 | assert_eq!(foo.field(), 13); 20 | 21 | *foo.field_mut() = 42; 22 | assert_eq!(foo.field_ref(), &42); 23 | ``` 24 | 25 | ## Auto derive traits 26 | 27 | Minimal set of whitelisted auto derived traits are supported. 28 | 29 | Don't forget you can implement additional methods and traits on the generated type! 30 | 31 | ``` 32 | #[struct_layout::explicit(size = 16, align = 4)] 33 | #[derive(Copy, Clone, Debug, Default)] 34 | struct Foo { 35 | #[field(offset = 4)] 36 | field: char, 37 | } 38 | 39 | let mut foo = Foo::default().clone(); 40 | let _ = foo; 41 | foo.set_field('a'); 42 | assert_eq!(format!("{:?}", foo), "Foo { field: 'a' }"); 43 | ``` 44 | 45 | ## Check argument to ensure safety 46 | 47 | The check attribute requires that all fields meet this trait bound. 48 | It is meant to support custom pod-like traits to ensure safe usage of the explicit attribute. 49 | 50 | ``` 51 | unsafe trait Pod {} 52 | unsafe impl Pod for i32 {} 53 | 54 | #[struct_layout::explicit(size = 16, align = 4, check(Pod))] 55 | struct Foo { 56 | #[field(offset = 4)] 57 | field: i32, 58 | } 59 | ``` 60 | 61 | ## Unaligned fields 62 | 63 | Annotate a field with `get` or `set` accessors allows unaligned fields. 64 | 65 | ``` 66 | #[struct_layout::explicit(size = 16, align = 4)] 67 | struct Foo { 68 | #[field(offset = 3, get, set)] 69 | field: i64, 70 | } 71 | ``` 72 | 73 | */ 74 | 75 | use std::vec; 76 | 77 | extern crate proc_macro; 78 | use proc_macro::*; 79 | 80 | //---------------------------------------------------------------- 81 | // Definitions 82 | 83 | #[derive(Clone, Debug)] 84 | struct KeyValue { 85 | ident: Ident, 86 | punct: Punct, 87 | value: Expr, 88 | } 89 | 90 | #[derive(Clone, Debug)] 91 | struct ExplicitLayout { 92 | size: Expr, 93 | align: Expr, 94 | check: Option, 95 | } 96 | 97 | #[derive(Clone, Debug)] 98 | struct FieldLayout { 99 | offset: Expr, 100 | method_get: bool, 101 | method_set: bool, 102 | method_ref: bool, 103 | method_mut: bool, 104 | } 105 | 106 | #[derive(Clone, Debug)] 107 | struct Meta { 108 | ident: Ident, 109 | args: Group, 110 | } 111 | 112 | #[derive(Clone, Debug)] 113 | struct Attribute { 114 | punct: Punct, 115 | meta: Group, 116 | } 117 | 118 | #[derive(Copy, Clone, Debug)] 119 | enum DerivedTrait { 120 | Copy, Clone, Debug, Default 121 | } 122 | 123 | #[derive(Clone, Debug)] 124 | struct Vis(Vec); 125 | 126 | #[derive(Clone, Debug)] 127 | struct Structure { 128 | attrs: Vec, 129 | derived: Vec, 130 | layout: ExplicitLayout, 131 | vis: Vis, 132 | stru: Ident, 133 | name: Ident, 134 | fields: Vec, 135 | } 136 | 137 | #[derive(Clone, Debug)] 138 | struct Type(Vec); 139 | #[derive(Clone, Debug)] 140 | struct Expr(TokenStream); 141 | 142 | #[derive(Clone, Debug)] 143 | struct Field { 144 | attrs: Vec, 145 | layout: FieldLayout, 146 | vis: Vis, 147 | name: Ident, 148 | ty: Type, 149 | } 150 | 151 | //---------------------------------------------------------------- 152 | // Lookahead queries 153 | 154 | fn is_ident(tokens: &[TokenTree]) -> bool { 155 | match tokens.first() { 156 | Some(TokenTree::Ident(_)) => true, 157 | _ => false, 158 | } 159 | } 160 | fn is_keyword(tokens: &[TokenTree], name: &str) -> bool { 161 | match tokens.first() { 162 | Some(TokenTree::Ident(ident)) => ident.to_string() == name, 163 | _ => false, 164 | } 165 | } 166 | fn is_punct(tokens: &[TokenTree], chr: char) -> bool { 167 | match tokens.first() { 168 | Some(TokenTree::Punct(punct)) => punct.as_char() == chr, 169 | _ => false, 170 | } 171 | } 172 | fn is_group(tokens: &[TokenTree], delim: Delimiter) -> bool { 173 | match tokens.first() { 174 | Some(TokenTree::Group(group)) => group.delimiter() == delim, 175 | _ => false, 176 | } 177 | } 178 | fn is_end(tokens: &[TokenTree]) -> bool { 179 | tokens.len() == 0 180 | } 181 | 182 | //---------------------------------------------------------------- 183 | // Parser elements 184 | 185 | fn parse_punct(tokens: &mut vec::IntoIter, punct: char) -> Option { 186 | if !is_punct(tokens.as_slice(), punct) { 187 | return None; 188 | } 189 | match tokens.next() { 190 | Some(TokenTree::Punct(punct)) => Some(punct), 191 | _ => unreachable!(), 192 | } 193 | } 194 | fn parse_ident(tokens: &mut vec::IntoIter) -> Option { 195 | match tokens.next() { 196 | Some(TokenTree::Ident(ident)) => Some(ident), 197 | _ => None, 198 | } 199 | } 200 | fn parse_keyword(tokens: &mut vec::IntoIter, keyword: &str) -> Option { 201 | if !is_keyword(tokens.as_slice(), keyword) { 202 | return None; 203 | } 204 | match tokens.next() { 205 | Some(TokenTree::Ident(ident)) => Some(ident), 206 | _ => unreachable!(), 207 | } 208 | } 209 | fn parse_group(tokens: &mut vec::IntoIter, delim: Delimiter) -> Option { 210 | if !is_group(tokens.as_slice(), delim) { 211 | return None; 212 | } 213 | match tokens.next() { 214 | Some(TokenTree::Group(group)) => Some(group), 215 | _ => unreachable!(), 216 | } 217 | } 218 | fn parse_comma(tokens: &mut vec::IntoIter) -> Option<()> { 219 | if is_end(tokens.as_slice()) { 220 | Some(()) 221 | } 222 | else if is_punct(tokens.as_slice(), ',') { 223 | let _ = tokens.next(); 224 | Some(()) 225 | } 226 | else { 227 | None 228 | } 229 | } 230 | fn parse_end(tokens: &mut vec::IntoIter) -> Option<()> { 231 | if tokens.len() != 0 { 232 | return None; 233 | } 234 | Some(()) 235 | } 236 | // $ident $group 237 | fn parse_meta(tokens: &mut vec::IntoIter) -> Option { 238 | let slice = tokens.as_slice(); 239 | if !(is_ident(&slice[0..]) && is_group(&slice[1..], Delimiter::Parenthesis)) { 240 | return None; 241 | } 242 | let ident = match tokens.next() { 243 | Some(TokenTree::Ident(ident)) => ident, 244 | _ => unreachable!(), 245 | }; 246 | let args = match tokens.next() { 247 | Some(TokenTree::Group(group)) => group, 248 | _ => unreachable!(), 249 | }; 250 | Some(Meta { ident, args }) 251 | } 252 | // $ident = $expr , 253 | fn parse_kv(tokens: &mut vec::IntoIter) -> Option { 254 | let slice = tokens.as_slice(); 255 | if !(is_ident(&slice[0..]) && is_punct(&slice[1..], '=')) { 256 | return None; 257 | } 258 | let ident = match tokens.next() { 259 | Some(TokenTree::Ident(ident)) => ident, 260 | _ => unreachable!(), 261 | }; 262 | let punct = match tokens.next() { 263 | Some(TokenTree::Punct(punct)) => punct, 264 | _ => unreachable!(), 265 | }; 266 | let value = parse_expr(tokens); 267 | Some(KeyValue { ident, punct, value }) 268 | } 269 | // # $group 270 | fn parse_attr(tokens: &mut vec::IntoIter) -> Option { 271 | if !is_punct(tokens.as_slice(), '#') { 272 | return None; 273 | } 274 | let punct = match tokens.next() { 275 | Some(TokenTree::Punct(punct)) => punct, 276 | _ => unreachable!(), 277 | }; 278 | let meta = match tokens.next() { 279 | Some(TokenTree::Group(group)) => group, 280 | _ => unreachable!(), 281 | }; 282 | Some(Attribute { punct, meta }) 283 | } 284 | // $(# $group)* 285 | fn parse_attrs(tokens: &mut vec::IntoIter) -> Vec { 286 | let mut attrs = Vec::new(); 287 | while let Some(attr) = parse_attr(tokens) { 288 | attrs.push(attr); 289 | } 290 | attrs 291 | } 292 | // $(pub $($group)?)? 293 | fn parse_vis(tokens: &mut vec::IntoIter) -> Vis { 294 | let mut vis = Vec::new(); 295 | if is_keyword(tokens.as_slice(), "pub") { 296 | vis.push(tokens.next().unwrap()); 297 | if is_group(tokens.as_slice(), Delimiter::Brace) { 298 | vis.push(tokens.next().unwrap()); 299 | } 300 | } 301 | Vis(vis) 302 | } 303 | // $($tt)*, 304 | fn parse_ty(tokens: &mut vec::IntoIter) -> Type { 305 | let mut ty = Vec::new(); 306 | let mut depth = 0; 307 | loop { 308 | match tokens.next() { 309 | Some(TokenTree::Punct(punct)) => { 310 | match punct.as_char() { 311 | ',' | ';' if depth == 0 => break, 312 | '<' => depth += 1, 313 | '>' => depth -= 1, 314 | _ => (), 315 | } 316 | ty.push(TokenTree::Punct(punct)); 317 | }, 318 | Some(tt) => ty.push(tt), 319 | None => break, 320 | } 321 | } 322 | Type(ty) 323 | } 324 | // $($tt)*, 325 | fn parse_expr(tokens: &mut vec::IntoIter) -> Expr { 326 | let mut expr = Vec::new(); 327 | loop { 328 | match tokens.next() { 329 | Some(TokenTree::Punct(punct)) => { 330 | if let ',' | ';' = punct.as_char() { 331 | break; 332 | } 333 | expr.push(TokenTree::Punct(punct)); 334 | }, 335 | Some(tt) => expr.push(tt), 336 | None => break, 337 | } 338 | } 339 | Expr(expr.into_iter().collect()) 340 | } 341 | 342 | //---------------------------------------------------------------- 343 | // Parse struct layout attribute 344 | 345 | fn parse_explicit_layout(tokens: TokenStream) -> ExplicitLayout { 346 | let tokens: Vec = tokens.into_iter().collect(); 347 | let mut tokens = tokens.into_iter(); 348 | let size = parse_layout_size(&mut tokens); 349 | let align = parse_layout_align(&mut tokens); 350 | let check = parse_layout_check(&mut tokens); 351 | parse_layout_end(&mut tokens); 352 | ExplicitLayout { size, align, check } 353 | } 354 | fn parse_layout_size(tokens: &mut vec::IntoIter) -> Expr { 355 | let size = match parse_kv(tokens) { 356 | Some(kv) => { 357 | if kv.ident.to_string() == "size" { kv.value } 358 | else { panic!("parse struct_layout: invalid format for size argument, expecting `size = `") } 359 | }, 360 | None => panic!("parse struct_layout: invalid format for size argument, expecting `size = `"), 361 | }; 362 | size 363 | } 364 | fn parse_layout_align(tokens: &mut vec::IntoIter) -> Expr { 365 | let align = match parse_kv(tokens) { 366 | Some(kv) => { 367 | if kv.ident.to_string() == "align" { kv.value } 368 | else { panic!("parse struct_layout: invalid format for align argument, expecting `align = `") } 369 | }, 370 | None => panic!("parse struct_layout: invalid format for align argument, expecting `align = `"), 371 | }; 372 | align 373 | } 374 | fn parse_layout_check(tokens: &mut vec::IntoIter) -> Option { 375 | let meta = parse_meta(tokens)?; 376 | if let None = parse_comma(tokens) { 377 | panic!("parse struct_layout: invalid format for check argument, expecting `check(PodTrait..)`"); 378 | } 379 | if meta.ident.to_string() != "check" { 380 | panic!("parse struct_layout: invalid format for check argument, expecting `check(PodTrait..)`"); 381 | } 382 | Some(meta.args.stream().to_string()) 383 | } 384 | fn parse_layout_end(tokens: &mut vec::IntoIter) { 385 | if let None = parse_end(tokens) { 386 | panic!("parse struct_layout: unexpected additional tokens found") 387 | } 388 | } 389 | 390 | //---------------------------------------------------------------- 391 | // Parse struct fields 392 | 393 | fn parse_fields(tokens: TokenStream) -> Vec { 394 | let tokens: Vec = tokens.into_iter().collect(); 395 | let mut tokens = tokens.into_iter(); 396 | let mut fields = Vec::new(); 397 | while tokens.len() > 0 { 398 | fields.push(parse_field(&mut tokens)); 399 | } 400 | fields 401 | } 402 | fn parse_field(tokens: &mut vec::IntoIter) -> Field { 403 | let mut attrs = parse_attrs(tokens); 404 | let layout = match parse_field_attrs(&mut attrs) { 405 | Some(layout) => layout, 406 | None => panic!("parse field: every field must have a `#[field(..)]` attribute"), 407 | }; 408 | let vis = parse_vis(tokens); 409 | let name = match parse_ident(tokens) { 410 | Some(ident) => ident, 411 | None => panic!("parse field: expecting field identifier not found"), 412 | }; 413 | if let None = parse_punct(tokens, ':') { 414 | panic!("parse field: colon must follow field identifier"); 415 | } 416 | let ty = parse_ty(tokens); 417 | Field { attrs, layout, vis, name, ty } 418 | } 419 | fn parse_field_attrs(attrs: &mut Vec) -> Option { 420 | let mut result = None; 421 | attrs.retain(|attr| { 422 | let tokens: Vec = attr.meta.stream().into_iter().collect(); 423 | let mut tokens = tokens.into_iter(); 424 | match tokens.as_slice().first() { 425 | Some(TokenTree::Ident(ident)) => { 426 | match &*ident.to_string() { 427 | "field" => { 428 | let meta = match parse_meta(&mut tokens) { 429 | Some(meta) => meta, 430 | None => panic!("parse field: invalid field attribute syntax, expecting `#[field(..)]`"), 431 | }; 432 | if let None = parse_end(&mut tokens) { 433 | panic!("parse field: found extra tokens after field attribute"); 434 | } 435 | let tokens: Vec = meta.args.stream().into_iter().collect(); 436 | let mut tokens = tokens.into_iter(); 437 | result = Some(parse_field_layout(&mut tokens)); 438 | false 439 | }, 440 | "doc" => true, 441 | s => panic!("parse field: unsupported attribute `{}`", s), 442 | } 443 | }, 444 | Some(_) => panic!("parse field: unknown attribute syntax"), 445 | None => false, 446 | } 447 | }); 448 | result 449 | } 450 | fn parse_field_layout(tokens: &mut vec::IntoIter) -> FieldLayout { 451 | let offset = match parse_kv(tokens) { 452 | Some(kv) => { 453 | if kv.ident.to_string() == "offset" { kv.value } 454 | else { panic!("parse field_layout: invalid format for offset argument, expecting `offset = `") } 455 | }, 456 | None => panic!("parse field_layout: invalid format for offset argument, expecting `offset = `"), 457 | }; 458 | let mut method_get = false; 459 | let mut method_set = false; 460 | let mut method_ref = false; 461 | let mut method_mut = false; 462 | while tokens.len() > 0 { 463 | let ident = match parse_ident(tokens) { 464 | Some(ident) => ident, 465 | None => panic!("parse field_layout: expecting an identifier"), 466 | }; 467 | let method = ident.to_string(); 468 | match &*method { 469 | "get" => method_get = true, 470 | "set" => method_set = true, 471 | "ref" => method_ref = true, 472 | "mut" => method_mut = true, 473 | _ => panic!("parse field_layout: expecting an identifier of `get`, `set`, `ref` or `mut`"), 474 | } 475 | if let None = parse_comma(tokens) { 476 | panic!("parse field_layout: expecting comma after {}", method); 477 | } 478 | } 479 | // If no methods are specified, enable all of them 480 | if !method_get && !method_set && !method_ref && !method_mut { 481 | method_get = true; 482 | method_set = true; 483 | method_ref = true; 484 | method_mut = true; 485 | } 486 | FieldLayout { offset, method_get, method_set, method_ref, method_mut } 487 | } 488 | 489 | //---------------------------------------------------------------- 490 | // Parse structure 491 | 492 | fn parse_structure(tokens: TokenStream, layout: ExplicitLayout) -> Structure { 493 | let tokens: Vec = tokens.into_iter().collect(); 494 | let mut tokens = tokens.into_iter(); 495 | let mut attrs = parse_attrs(&mut tokens); 496 | let derived = parse_structure_attrs(&mut attrs); 497 | let vis = parse_vis(&mut tokens); 498 | let stru = match parse_keyword(&mut tokens, "struct") { 499 | Some(ident) => ident, 500 | None => panic!("parse struct: struct layout is only allowed on structs"), 501 | }; 502 | let name = match parse_ident(&mut tokens) { 503 | Some(ident) => ident, 504 | None => panic!("parse struct: struct name identifier not found"), 505 | }; 506 | if is_punct(tokens.as_slice(), '<') { 507 | panic!("parse struct: generic parameters not supported"); 508 | } 509 | if is_keyword(tokens.as_slice(), "where") { 510 | panic!("parse struct: where clause not supported"); 511 | } 512 | let group = match parse_group(&mut tokens, Delimiter::Brace) { 513 | Some(group) => group, 514 | None => panic!("parse struct: tuple syntax not supported, struct layout requires {{}} to declare the fields"), 515 | }; 516 | let fields = parse_fields(group.stream()); 517 | Structure { attrs, derived, layout, vis, stru, name, fields } 518 | } 519 | fn parse_structure_attrs(attrs: &mut Vec) -> Vec { 520 | let mut result = Vec::new(); 521 | attrs.retain(|attr| { 522 | let tokens: Vec = attr.meta.stream().into_iter().collect(); 523 | let mut tokens = tokens.into_iter(); 524 | match tokens.as_slice().first() { 525 | Some(TokenTree::Ident(ident)) => { 526 | match &*ident.to_string() { 527 | "derive" => { 528 | let meta = match parse_meta(&mut tokens) { 529 | Some(meta) => meta, 530 | None => panic!("parse struct: invalid derive syntax, expecting `#[derive(..)]`"), 531 | }; 532 | if let None = parse_end(&mut tokens) { 533 | panic!("parse struct: found extra tokens after derive attribute"); 534 | } 535 | let tokens: Vec = meta.args.stream().into_iter().collect(); 536 | let mut tokens = tokens.into_iter(); 537 | parse_structure_derive(&mut tokens, &mut result); 538 | false 539 | }, 540 | "doc" => true, 541 | s => panic!("parse struct: unsupported attribute `{}`", s), 542 | } 543 | }, 544 | Some(_) => panic!("parse struct: unknown attribute syntax"), 545 | None => false, 546 | } 547 | }); 548 | result 549 | } 550 | fn parse_structure_derive(tokens: &mut vec::IntoIter, derived: &mut Vec) { 551 | while tokens.len() > 0 { 552 | let ident = match parse_ident(tokens) { 553 | Some(ident) => ident, 554 | None => panic!("derive attribute: expecting list of comma separated identifiers"), 555 | }; 556 | let tr = ident.to_string(); 557 | match &*tr { 558 | "Copy" => derived.push(DerivedTrait::Copy), 559 | "Clone" => derived.push(DerivedTrait::Clone), 560 | "Debug" => derived.push(DerivedTrait::Debug), 561 | "Default" => derived.push(DerivedTrait::Default), 562 | s => panic!("derive attribute: unsupported trait: `{}`", s), 563 | } 564 | if let None = parse_comma(tokens) { 565 | panic!("derive attribute: expecting comma after {}", tr); 566 | } 567 | } 568 | } 569 | 570 | //---------------------------------------------------------------- 571 | 572 | /// Explicit field layout attribute. 573 | /// 574 | /// For more information, see the crate-level documentation. 575 | #[proc_macro_attribute] 576 | pub fn explicit(attributes: TokenStream, input: TokenStream) -> TokenStream { 577 | let layout = parse_explicit_layout(attributes); 578 | let stru = parse_structure(input, layout); 579 | // Emit the code 580 | let mut code: Vec = Vec::new(); 581 | emit_attrs(&mut code, &stru.attrs); 582 | emit_text(&mut code, &format!("#[repr(C, align({}))]", stru.layout.align.0)); 583 | emit_vis(&mut code, &stru.vis); 584 | code.push(TokenTree::Ident(stru.stru.clone())); 585 | code.push(TokenTree::Ident(stru.name.clone())); 586 | emit_text(&mut code, &format!("([u8; {}]);", stru.layout.size.0)); 587 | emit_impl_f(&mut code, &stru.name, |body| { 588 | for field in &stru.fields { 589 | emit_field(body, &stru, field); 590 | } 591 | }); 592 | emit_derives(&mut code, &stru); 593 | code.into_iter().collect() 594 | } 595 | 596 | //---------------------------------------------------------------- 597 | // Emitters 598 | 599 | fn emit_punct(code: &mut Vec, ch: char) { 600 | code.push(TokenTree::Punct(Punct::new(ch, Spacing::Alone))); 601 | } 602 | fn emit_ident(code: &mut Vec, name: &str) { 603 | code.push(TokenTree::Ident(Ident::new(name, Span::call_site()))); 604 | } 605 | fn emit_text(code: &mut Vec, text: &str) { 606 | let stream: TokenStream = text.parse().unwrap(); 607 | code.extend(stream); 608 | } 609 | fn emit_group_f(code: &mut Vec, delim: Delimiter, f: impl FnOnce(&mut Vec)) { 610 | let mut tokens = Vec::new(); 611 | f(&mut tokens); 612 | let stream: TokenStream = tokens.into_iter().collect(); 613 | code.push(TokenTree::Group(Group::new(delim, stream))); 614 | } 615 | 616 | fn emit_attrs(code: &mut Vec, attrs: &[Attribute]) { 617 | for attr in attrs { 618 | code.push(TokenTree::Punct(attr.punct.clone())); 619 | code.push(TokenTree::Group(attr.meta.clone())); 620 | } 621 | } 622 | fn emit_vis(code: &mut Vec, vis: &Vis) { 623 | code.extend(vis.0.iter().cloned()); 624 | } 625 | fn emit_ty(code: &mut Vec, ty: &Type) { 626 | code.extend(ty.0.iter().cloned()); 627 | } 628 | fn emit_impl_f(code: &mut Vec, ident: &Ident, f: impl FnOnce(&mut Vec)) { 629 | code.push(TokenTree::Ident(Ident::new("impl", Span::call_site()))); 630 | code.push(TokenTree::Ident(ident.clone())); 631 | code.push(TokenTree::Group(Group::new(Delimiter::Brace, { 632 | let mut tokens = Vec::new(); 633 | f(&mut tokens); 634 | tokens.into_iter().collect() 635 | }))); 636 | } 637 | fn emit_trait_bounds(code: &mut Vec, stru: &Structure, tr: &str) { 638 | if stru.fields.len() > 0 { 639 | emit_ident(code, "where"); 640 | let bound = format!(": {},", tr); 641 | for field in &stru.fields { 642 | emit_ty(code, &field.ty); 643 | emit_text(code, &bound); 644 | } 645 | } 646 | } 647 | fn emit_trait_impl_f(code: &mut Vec, stru: &Structure, tr: &str, f: impl FnOnce(&mut Vec)) { 648 | code.push(TokenTree::Ident(Ident::new("impl", Span::call_site()))); 649 | emit_text(code, tr); 650 | code.push(TokenTree::Ident(Ident::new("for", Span::call_site()))); 651 | code.push(TokenTree::Ident(stru.name.clone())); 652 | emit_trait_bounds(code, stru, tr); 653 | code.push(TokenTree::Group(Group::new(Delimiter::Brace, { 654 | let mut tokens = Vec::new(); 655 | f(&mut tokens); 656 | tokens.into_iter().collect() 657 | }))); 658 | } 659 | 660 | fn emit_derive_copy(code: &mut Vec, stru: &Structure) { 661 | emit_trait_impl_f(code, stru, "Copy", |_| {}); 662 | } 663 | fn emit_derive_clone(code: &mut Vec, stru: &Structure) { 664 | emit_trait_impl_f(code, stru, "Clone", |code| { 665 | emit_text(code, "fn clone(&self) -> Self { *self }"); 666 | }) 667 | } 668 | fn emit_derive_debug(code: &mut Vec, stru: &Structure) { 669 | emit_trait_impl_f(code, stru, "::core::fmt::Debug", |code| { 670 | emit_text(code, "fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result"); 671 | emit_group_f(code, Delimiter::Brace, |code| { 672 | emit_text(code, &format!("f.debug_struct(\"{}\")", &stru.name)); 673 | for field in &stru.fields { 674 | if field.layout.method_ref { 675 | emit_text(code, &format!(".field(\"{0}\", self.{0}_ref())", field.name)); 676 | } 677 | else if field.layout.method_get { 678 | emit_text(code, &format!(".field(\"{0}\", &self.{0}())", field.name)); 679 | } 680 | } 681 | emit_text(code, ".finish()"); 682 | }); 683 | }); 684 | } 685 | fn emit_derive_default(code: &mut Vec, stru: &Structure) { 686 | emit_trait_impl_f(code, stru, "Default", |code| { 687 | emit_text(code, "fn default() -> Self"); 688 | emit_group_f(code, Delimiter::Brace, |code| { 689 | emit_text(code, "let mut instance: Self = unsafe { ::core::mem::zeroed() };"); 690 | for field in &stru.fields { 691 | emit_text(code, &format!("instance.set_{}(Default::default());", field.name)); 692 | } 693 | emit_text(code, "; instance"); 694 | }); 695 | }); 696 | } 697 | fn emit_derives(code: &mut Vec, stru: &Structure) { 698 | for derive in &stru.derived { 699 | match derive { 700 | DerivedTrait::Copy => emit_derive_copy(code, stru), 701 | DerivedTrait::Clone => emit_derive_clone(code, stru), 702 | DerivedTrait::Debug => emit_derive_debug(code, stru), 703 | DerivedTrait::Default => emit_derive_default(code, stru), 704 | } 705 | } 706 | } 707 | fn emit_field(code: &mut Vec, stru: &Structure, field: &Field) { 708 | if field.layout.method_get { 709 | emit_field_get(code, stru, field); 710 | } 711 | if field.layout.method_set { 712 | emit_field_set(code, stru, field); 713 | } 714 | if field.layout.method_ref { 715 | emit_field_ref(code, stru, field); 716 | } 717 | if field.layout.method_mut { 718 | emit_field_mut(code, stru, field); 719 | } 720 | } 721 | fn emit_field_get(code: &mut Vec, stru: &Structure, field: &Field) { 722 | emit_attrs(code, &field.attrs); 723 | emit_vis(code, &field.vis); 724 | emit_ident(code, "fn"); 725 | code.push(TokenTree::Ident(field.name.clone())); 726 | emit_text(code, "(&self) -> "); 727 | emit_ty(code, &field.ty); 728 | emit_field_check(code, stru, field); 729 | emit_group_f(code, Delimiter::Brace, |body| { 730 | emit_text(body, &format!("const FIELD_OFFSET: usize = {};", field.layout.offset.0)); 731 | emit_text(body, "type FieldT = "); emit_ty(body, &field.ty); 732 | emit_text(body, "; use ::core::{mem, ptr}; let _: [(); 733 | (FIELD_OFFSET + mem::size_of::() <= mem::size_of::()) as usize - 1];"); 734 | emit_text(body, "unsafe { ptr::read_unaligned((self as *const _ as *const u8).offset(FIELD_OFFSET as isize) as *const FieldT) }"); 735 | }); 736 | } 737 | fn emit_field_set(code: &mut Vec, stru: &Structure, field: &Field) { 738 | emit_attrs(code, &field.attrs); 739 | emit_vis(code, &field.vis); 740 | emit_ident(code, "fn"); 741 | emit_ident(code, &format!("set_{}", field.name)); 742 | emit_group_f(code, Delimiter::Parenthesis, |params| { 743 | emit_text(params, "&mut self, value: "); 744 | emit_ty(params, &field.ty); 745 | }); 746 | emit_text(code, " -> &mut Self"); 747 | emit_field_check(code, stru, field); 748 | emit_group_f(code, Delimiter::Brace, |body| { 749 | emit_text(body, &format!("const FIELD_OFFSET: usize = {};", field.layout.offset.0)); 750 | emit_text(body, "type FieldT = "); emit_ty(body, &field.ty); 751 | emit_text(body, "; use ::core::{mem, ptr}; let _: [(); 752 | (FIELD_OFFSET + mem::size_of::() <= mem::size_of::()) as usize - 1];"); 753 | emit_text(body, "unsafe { ptr::write_unaligned((self as *mut _ as *mut u8).offset(FIELD_OFFSET as isize) as *mut FieldT, value); }"); 754 | emit_ident(body, "self"); 755 | }) 756 | } 757 | fn emit_field_ref(code: &mut Vec, stru: &Structure, field: &Field) { 758 | emit_attrs(code, &field.attrs); 759 | emit_vis(code, &field.vis); 760 | emit_text(code, &format!("fn {}_ref(&self) -> &", field.name)); 761 | emit_ty(code, &field.ty); 762 | emit_field_check(code, stru, field); 763 | emit_group_f(code, Delimiter::Brace, |body| { 764 | emit_text(body, &format!("const FIELD_OFFSET: usize = {};", field.layout.offset.0)); 765 | emit_text(body, "type FieldT = "); emit_ty(body, &field.ty); 766 | emit_text(body, "; use ::core::mem; let _: [(); 767 | (FIELD_OFFSET + mem::size_of::() <= mem::size_of::() && 768 | FIELD_OFFSET % mem::align_of::() == 0 && 769 | mem::align_of::() % mem::align_of::() == 0) as usize - 1];"); 770 | emit_text(body, "unsafe { &*((self as *const _ as *const u8).offset(FIELD_OFFSET as isize) as *const FieldT) }"); 771 | }); 772 | } 773 | fn emit_field_mut(code: &mut Vec, stru: &Structure, field: &Field) { 774 | emit_attrs(code, &field.attrs); 775 | emit_vis(code, &field.vis); 776 | emit_text(code, &format!("fn {}_mut(&mut self) -> &mut ", field.name)); 777 | emit_ty(code, &field.ty); 778 | emit_field_check(code, stru, field); 779 | emit_group_f(code, Delimiter::Brace, |body| { 780 | emit_text(body, &format!("const FIELD_OFFSET: usize = {};", field.layout.offset.0)); 781 | emit_text(body, "type FieldT = "); emit_ty(body, &field.ty); 782 | emit_text(body, "; use ::core::mem; let _: [(); 783 | (FIELD_OFFSET + mem::size_of::() <= mem::size_of::() && 784 | FIELD_OFFSET % mem::align_of::() == 0 && 785 | mem::align_of::() % mem::align_of::() == 0) as usize - 1];"); 786 | emit_text(body, "unsafe { &mut *((self as *mut _ as *mut u8).offset(FIELD_OFFSET as isize) as *mut FieldT) }"); 787 | }); 788 | } 789 | fn emit_field_check(code: &mut Vec, stru: &Structure, field: &Field) { 790 | let check = stru.layout.check.as_ref().map(std::ops::Deref::deref).unwrap_or("Copy + 'static"); 791 | emit_ident(code, "where"); 792 | emit_ty(code, &field.ty); 793 | emit_punct(code, ':'); 794 | emit_text(code, check); 795 | } 796 | 797 | //---------------------------------------------------------------- 798 | 799 | /// The following are incorrect usage of the explicit attribute. 800 | /// 801 | /// ```compile_fail 802 | /// #[struct_layout::explicit] 803 | /// struct Foo {} 804 | /// ``` 805 | /// 806 | /// Missing required arguments. 807 | /// 808 | /// ```compile_fail 809 | /// #[struct_layout::explicit(size = 8, align = 4)] 810 | /// enum Foo {} 811 | /// ``` 812 | /// 813 | /// The explicit attribute only works on struct definitions. 814 | /// 815 | /// ```compile_fail 816 | /// #[struct_layout::explicit(size = 8, align = 4)] 817 | /// struct Foo { 818 | /// field: i32, 819 | /// } 820 | /// ``` 821 | /// 822 | /// All fields must have exactly one `#[field]` attribute. 823 | /// 824 | /// ```compile_fail 825 | /// #[struct_layout::explicit(size = 8, align = 4)] 826 | /// struct Foo { 827 | /// #[field(offset = 1)] 828 | /// field: i64, 829 | /// } 830 | /// ``` 831 | /// 832 | /// Field does not meet alignment requirements. 833 | /// 834 | /// ```compile_fail 835 | /// #[struct_layout::explicit(size = 8, align = 4)] 836 | /// struct Foo { 837 | /// #[field(offset = 5, get, set)] 838 | /// field: i32, 839 | /// } 840 | /// ``` 841 | /// 842 | /// Field out of bounds. 843 | /// 844 | /// ```compile_fail 845 | /// unsafe trait Pod {} 846 | /// 847 | /// #[struct_layout::explicit(size = 8, align = 4, check(Pod))] 848 | /// struct Foo { 849 | /// #[field(offset = 4)] 850 | /// field: i32, 851 | /// } 852 | /// ``` 853 | /// 854 | /// Field type does not satisfy Pod constraint. 855 | /// 856 | /// ```compile_fail 857 | /// #[struct_layout::explicit(size = 8, align = 4)] 858 | /// #[repr(C)] 859 | /// struct Foo {} 860 | /// ``` 861 | /// 862 | /// ```compile_fail 863 | /// #[struct_layout::explicit(size = 8, align = 4)] 864 | /// struct Foo { 865 | /// #[field(offset = 4)] 866 | /// #[allow(bad_style)] 867 | /// Field: i32, 868 | /// } 869 | /// ``` 870 | /// 871 | /// Unsupported attributes. 872 | #[allow(dead_code)] 873 | fn compile_fail() {} 874 | --------------------------------------------------------------------------------