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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
/*! Custom Derive for the Endian Trait

This crate provides a custom-derive procedural macro for the Endian trait. This
macro simply takes the component fields of the type on which it is annotated and
attempts to call the Endian conversion on each field. This means that Endian can
only be derived on types composed of types that are all themselves Endian.
Attempting to derive Endian on a type with a non-Endian type in it will result
in a compilation error.

# Use Case

Network serialization of complex types. That's pretty much it. This trait does
not require explicitly, but in practice basically does need, that the types on
which it is implemented are `repr(C)` or `repr(packed)`. You can get away with
using it on types that do not have that representation if you can guarantee that
you know how to properly handle the actual byte representation and the generator
and consumer are the same Rust version.

# Usage

This crate shouldn't be used directly; use

```rust,no-run
#[macro_use]
extern crate endian_trait;
# fn main() {}
```

and this crate will be pulled in transitively.

By itself, this crate provides a custom-derive macro to emit a trait impl block
that is syntactically valid but will fail to compile without the `endian_trait`
crate and `Endian` trait in scope.

```rust
#[macro_use]
extern crate endian_trait;
# // This is needed because of the fact that the test is executed from within
# // the context of the derive crate. The tests in the trait crate demonstrate
# // that the macro is correctly re-exported.
# #[macro_use]
# extern crate endian_trait_derive;

use endian_trait::Endian;

#[derive(Endian)]
struct Foo<A: Endian, B: Endian> {
    bar: A,
    baz: B,
}
# fn main() {}
```
!*/

extern crate proc_macro;
#[macro_use]
extern crate quote;
extern crate syn;

use proc_macro::TokenStream;
use quote::{
	Tokens,
	ToTokens,
};
use syn::{
	Attribute,
	Data,
	DataStruct,
	DeriveInput,
	Fields,
	FieldsNamed,
	FieldsUnnamed,
	Generics,
	Ident,
	Index,
	Meta,
	MetaList,
	NestedMeta,
};

/// Hook for receiving `#[derive(Endian)]` code
#[proc_macro_derive(Endian)]
pub fn endian_trait(source: TokenStream) -> TokenStream {
	//  A parse failure means that the input was invalid Rust. This is not our
	//  problem, so a panic is permissible.
	let ast = syn::parse(source).unwrap();
	//  Take the parsed AST and pass it into the actual worker function, then
	//  convert the results back into Rust tokens.
	impl_endian(ast).into()
}

/// Code generator for Endian implementations
fn impl_endian(ast: DeriveInput) -> Tokens {
	//  Get the name of the struct on which Endian is to be implemented
	let name: &Ident = &ast.ident;
	//  Get any generics from the struct
	let generics: &Generics = &ast.generics;
	match ast.data {
		//  Attempt to derive Endian for an integer-repr enum
		Data::Enum(_) => codegen_enum(name, &ast.attrs),
		Data::Struct(DataStruct { ref fields, .. }) =>  match *fields {
			//  Normal struct: named fields
			Fields::Named(FieldsNamed { ref named, .. }) => {
				//  Collect references to all the field names. A precondition of
				//  reaching this code path is that all fields HAVE names, so it
				//  is safe to have an unreachable trap in the None condition.
				let names: Vec<&Ident> = named.iter().map(|f| match f.ident {
					Some(ref n) => n,
					None => unreachable!("All fields in a struct must be named"),
				}).collect();
				codegen_struct(name, generics, &names)
			},
			//  Tuple struct: unnamed fields
			Fields::Unnamed(FieldsUnnamed { ref unnamed, .. }) => {
				let nums: Vec<Index> = (0 .. unnamed.len()).map(|n| n.into())
					.collect();
				codegen_struct(name, generics, &nums)
			},
			//  Unit struct: no fields
			//  This is simple: all Endian functions are just the identity
			//  function. Note that Unit structs are a subset of zero-sized
			//  types: specifically, unit has zero fields, while ZST is the set
			//  of all structs with zero or more fields which are all of zero
			//  size. Unit structs are specifically:
			//
			//  - ()
			//  - struct Foo;
			//  - struct Foo();
			//  - struct Foo{};
			//
			//  All ZST types eventually bottom out here; the interim can be
			//  handled by the other branches.
			//
			//  RFC #1506 (stabilized in 1.19) permits unit structs to be
			//  declared as `Foo {}`. This allows for a deduplication of the
			//  code generation path: the codegen function emits
			//
			//  fn func(self) -> Self { Self { /* fields */ } }
			//
			//  which, when `fields` is empty, leaves `Self { }` as the output
			//  token. Since this is legal, there is no special case for
			//  handling empty field sets.
			Fields::Unit => codegen_struct::<Index>(name, generics, &[]),
		},
		Data::Union(_) => {
			unimplemented!("Proc-macro derives are not yet allowed on unions");
		},
	}
}

/// Generate the Endian impl for an enum with an integer repr and no data body.
fn codegen_enum(name: &Ident, attrs: &[Attribute]) -> Tokens {
	//  Find the attr that is #[repr(_)]. We need to build one for comparison.
	let repr_path: syn::Path = Ident::from("repr").into();
	//  Seek for a #[repr(_)] attribute
	let repr: &Meta = &attrs.iter().find(|ref a| &a.path == &repr_path)
	//  Unwrap, and panic if this returned None instead of Some, because repr is
	//  the bare minimum of required attributes
	.expect("Endian can only be derived on enums with #[repr()] attributes")
	//  Take a reference to the actual value of the attribute, which is
	//  "repr(_)" from the source.
	.interpret_meta()
	.expect("#[repr(_)] cannot fail to be interpreted");
	//  Now figure out what the repr *is*. The format is #[repr(Name)] where
	//  Name is one of {i,u}{8,16,32,64}. This comes out to be a
	//  List(MetaList(Vec<Meta>, ..)) in syn structures. We want the one-element
	//  Vec's one element. Anything else is broken.
	let kind: &Ident = match *repr {
		Meta::List(MetaList { ref nested, .. }) => {
			if nested.len() != 1 {
				panic!("The #[repr()] attribute must be a single primitive integer type");
			}
			match nested[0] {
				NestedMeta::Meta(Meta::Word(ref ty)) => ty,
				_ => panic!("The #[repr()] interior must be a primitive integer type"),
			}
		},
		_ => unreachable!("The #[repr()] interior must be a primitive integer type"),
	};
	if kind == &Ident::from("C") {
		panic!("#[repr(C)] enums cannot implement Endian");
	}
	else if kind == &Ident::from("packed") {
		panic!("#[repr(packed)] enums cannot implement Endian");
	}
	quote! {
		impl Endian for #name {
			fn from_be(self) -> Self { unsafe {
				use std::mem::transmute;
				let raw: #kind = transmute(self);
				transmute(raw.from_be())
			} }
			fn from_le(self) -> Self { unsafe {
				use std::mem::transmute;
				let raw: #kind = transmute(self);
				transmute(raw.from_le())
			} }
			fn to_be(self) -> Self { unsafe {
				use std::mem::transmute;
				let raw: #kind = transmute(self);
				transmute(raw.to_be())
			} }
			fn to_le(self) -> Self { unsafe {
				use std::mem::transmute;
				let raw: #kind = transmute(self);
				transmute(raw.to_le())
			} }
		}
	}
}

/// Generates Rust code to impl Endian on the given struct declaration.
///
/// This requires the struct's name, its generic information (if any), and its
/// fields (if any).
///
/// Thanks to RFC #1506 (stabilized in 1.19), this can be a single code path for
/// all three types of struct definition: standard, tuple, and unit.
fn codegen_struct<T: ToTokens>(name: &Ident, generics: &Generics, fields: &[T]) -> Tokens {
	//  We need left and right access to each name so that the stepper can draw
	//  from each once, rather than advancing the name iterator twice per step.
	//  Without this, the stepper would have incorrect behavior consuming the
	//  old field and inserting it into the new.
	let (l, r) = (fields, fields);
	//  Split the type's generics into appropriate forms for the impl block.
	let (g_impl, g_ty, g_where) = generics.split_for_impl();
	//  Build Rust code that is the impl block and each function, consuming self
	//  and building a new Self instance that is the result of applying the
	//  Endian conversion to each field.
	quote! {
		impl #g_impl Endian for #name #g_ty #g_where {
			fn from_be(self) -> Self { Self {
				#( #l: Endian::from_be(self.#r), )*
			} }
			fn from_le(self) -> Self { Self {
				#( #l: Endian::from_le(self.#r), )*
			} }
			fn to_be(self) -> Self { Self {
				#( #l: Endian::to_be(self.#r), )*
			} }
			fn to_le(self) -> Self { Self {
				#( #l: Endian::to_le(self.#r), )*
			} }
		}
	}
}