bytify_impl/
lib.rs

1extern crate proc_macro;
2
3use std::io::{Error as IOError};
4use byteorder::{ByteOrder, WriteBytesExt, BE, LE};
5use failure::Fail;
6use quote::{ToTokens, quote};
7use proc_macro::TokenStream;
8use proc_macro_hack::proc_macro_hack;
9use syn::{parse_macro_input, Error as SynError, Expr, IntSuffix, FloatSuffix, Lit, LitInt, LitFloat, Token, UnOp};
10use syn::parse::{Parse, ParseStream};
11use syn::punctuated::Punctuated;
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14enum Endianness {
15    LE,
16    BE,
17}
18
19#[cfg(not(feature = "default-big-endian"))]
20const DEFAULT_ENDIANNESS: Endianness = Endianness::LE;
21
22#[cfg(feature = "default-big-endian")]
23const DEFAULT_ENDIANNESS: Endianness = Endianness::BE;
24
25#[derive(Debug, Fail)]
26enum Error {
27    #[fail(display = "Unsupported prefixed expression in the macro: {} [+] {}", _0, _1)]
28    UnsupportedPrefixedExpression(String, String),
29    #[fail(display = "Unsupported expression in the macro: {}", _0)]
30    UnsupportedExpression(String),
31    #[fail(display = "Unsupported literal in the macro: {}", _0)]
32    UnsupportedLit(String),
33    #[fail(display = "Unsupported numeric suffix in the macro: {}", _0)]
34    UnsupportedNumberSuffix(String),
35    #[fail(display = "Failed to parse the input as a comma-separated list: {}", _0)]
36    InvalidInput(#[cause] SynError),
37    #[fail(display = "Failed to parse endianness: {}", _0)]
38    InvalidEndianness(String),
39    #[fail(display = "Failed to write a suffixed value: {}, negative: {}, given suffix: {}, requested suffix: {}", _0, _1, _2, _3)]
40    IncompatibleNumberSuffix(String, bool, String, String),
41    #[fail(display = "Failed to write a value: {}", _0)]
42    IO(#[cause] IOError),
43}
44
45impl From<SynError> for Error {
46
47    fn from(from: SynError) -> Self {
48        Error::InvalidInput(from)
49    }
50}
51
52impl From<IOError> for Error {
53
54    fn from(from: IOError) -> Self {
55        Error::IO(from)
56    }
57}
58
59impl Error {
60
61    pub fn unsupported_expression(expr: Expr) -> Self {
62        Error::UnsupportedExpression(expr.into_token_stream().to_string())
63    }
64
65    pub fn unsupported_lit(lit: Lit) -> Self {
66        Error::UnsupportedLit(lit.into_token_stream().to_string())
67    }
68
69    pub fn unsupported_prefixed_expression(op: UnOp, expr: Expr) -> Self {
70        Error::UnsupportedPrefixedExpression(op.into_token_stream().to_string(), expr.into_token_stream().to_string())
71    }
72}
73
74fn int_to_suffix(negative: bool, int: &LitInt) -> Result<IntSuffix, Error> {
75    let num_bits = int.value();
76    let s = if negative {
77        match () {
78            () if num_bits > 0x80000000 => IntSuffix::I64,
79            () if num_bits > 0x8000     => IntSuffix::I32,
80            () if num_bits > 0x80       => IntSuffix::I16,
81            () => IntSuffix::I8,
82        }
83    } else {
84        match () {
85            () if num_bits > 0xFFFFFFFF => IntSuffix::U64,
86            () if num_bits > 0xFFFF     => IntSuffix::U32,
87            () if num_bits > 0xFF       => IntSuffix::U16,
88            () => IntSuffix::U8,
89        }
90    };
91    let s = match (s, int.suffix()) {
92        // If none is specified use the least size suffix possible.
93        (s, IntSuffix::None) => s,
94        // Allowed casts Uint -> Uint.
95        (IntSuffix::U8 , IntSuffix::U8 ) => IntSuffix::U8 ,
96        (IntSuffix::U8 , IntSuffix::U16) => IntSuffix::U16,
97        (IntSuffix::U8 , IntSuffix::U32) => IntSuffix::U32,
98        (IntSuffix::U8 , IntSuffix::U64) => IntSuffix::U64,
99        (IntSuffix::U16, IntSuffix::U16) => IntSuffix::U16,
100        (IntSuffix::U16, IntSuffix::U32) => IntSuffix::U32,
101        (IntSuffix::U16, IntSuffix::U64) => IntSuffix::U64,
102        (IntSuffix::U32, IntSuffix::U32) => IntSuffix::U32,
103        (IntSuffix::U32, IntSuffix::U64) => IntSuffix::U64,
104        (IntSuffix::U64, IntSuffix::U64) => IntSuffix::U64,
105        // Allowed casts Sint -> Sint.
106        (IntSuffix::I8 , IntSuffix::I8 ) => IntSuffix::I8 ,
107        (IntSuffix::I8 , IntSuffix::I16) => IntSuffix::I16,
108        (IntSuffix::I8 , IntSuffix::I32) => IntSuffix::I32,
109        (IntSuffix::I8 , IntSuffix::I64) => IntSuffix::I64,
110        (IntSuffix::I16, IntSuffix::I16) => IntSuffix::I16,
111        (IntSuffix::I16, IntSuffix::I32) => IntSuffix::I32,
112        (IntSuffix::I16, IntSuffix::I64) => IntSuffix::I64,
113        (IntSuffix::I32, IntSuffix::I32) => IntSuffix::I32,
114        (IntSuffix::I32, IntSuffix::I64) => IntSuffix::I64,
115        (IntSuffix::I64, IntSuffix::I64) => IntSuffix::I64,
116        // Allowed casts Uint -> Sint.
117        (IntSuffix::U8 , IntSuffix::I8 ) if num_bits < 0x80               => IntSuffix::I8 ,
118        (IntSuffix::U16, IntSuffix::I16) if num_bits < 0x8000             => IntSuffix::I16,
119        (IntSuffix::U32, IntSuffix::I32) if num_bits < 0x80000000         => IntSuffix::I32,
120        (IntSuffix::U64, IntSuffix::I64) if num_bits < 0x8000000000000000 => IntSuffix::I64,
121        (IntSuffix::U8 , IntSuffix::I16) => IntSuffix::I16,
122        (IntSuffix::U8 , IntSuffix::I32) => IntSuffix::I32,
123        (IntSuffix::U8 , IntSuffix::I64) => IntSuffix::I64,
124        (IntSuffix::U16, IntSuffix::I32) => IntSuffix::I32,
125        (IntSuffix::U16, IntSuffix::I64) => IntSuffix::I64,
126        (IntSuffix::U32, IntSuffix::I64) => IntSuffix::I64,
127        // Everything else is either invalid or ambiguous.
128        (given, requested) => {
129            return Err(Error::IncompatibleNumberSuffix(
130                int.into_token_stream().to_string(),
131                negative,
132                format!("{:?}", given),
133                format!("{:?}", requested),
134            ));
135        },
136    };
137    Ok(s)
138}
139
140fn bytify_implementation_int<O: ByteOrder>(negative: bool, int: LitInt, output: &mut Vec<u8>) -> Result<(), Error> {
141    let num_bits = int.value();
142    let num_bits_suffix = int_to_suffix(negative, &int)?;
143    match num_bits_suffix {
144        IntSuffix::U8 => {
145            output.write_u8(num_bits as u8)?;
146        },
147        IntSuffix::I8 => {
148            if negative {
149                output.write_u8((!(num_bits as u8)).wrapping_add(1))?;
150            } else {
151                output.write_u8(   num_bits as u8)?;
152            }
153        },
154        IntSuffix::U16 => {
155            output.write_u16::<O>(num_bits as u16)?;
156        },
157        IntSuffix::I16 => {
158            if negative {
159                output.write_u16::<O>((!(num_bits as u16)).wrapping_add(1))?;
160            } else {
161                output.write_u16::<O>(   num_bits as u16)?;
162            }
163        },
164        IntSuffix::U32 => {
165            output.write_u32::<O>(num_bits as u32)?;
166        },
167        IntSuffix::I32 => {
168            if negative {
169                output.write_u32::<O>((!(num_bits as u32)).wrapping_add(1))?;
170            } else {
171                output.write_u32::<O>(   num_bits as u32)?;
172            }
173        },
174        IntSuffix::U64 => {
175            output.write_u64::<O>(num_bits as u64)?;
176        },
177        IntSuffix::I64 => {
178            if negative {
179                output.write_u64::<O>((!(num_bits as u64)).wrapping_add(1))?;
180            } else {
181                output.write_u64::<O>(   num_bits as u64)?;
182            }
183        },
184        // Everything else is either invalid or ambiguous.
185        s => {
186            return Err(Error::UnsupportedNumberSuffix(format!("{:?}", s)));
187        },
188    }
189    Ok(())
190}
191
192fn float_to_suffix(negative: bool, float: &LitFloat) -> Result<FloatSuffix, Error> {
193    let num_bits = float.value();
194    let s = if num_bits > 3.40282347e+38 {
195        FloatSuffix::F64
196    } else {
197        FloatSuffix::F32
198    };
199    let s = match (s, float.suffix()) {
200        // If none is specified use the least size suffix possible.
201        (s, FloatSuffix::None) => s,
202        // The only possible float cast.
203        (FloatSuffix::F32, FloatSuffix::F64) => FloatSuffix::F64,
204        // Everything else is either invalid or ambiguous.
205        (given, requested) => {
206            return Err(Error::IncompatibleNumberSuffix(
207                float.into_token_stream().to_string(),
208                negative,
209                format!("{:?}", given),
210                format!("{:?}", requested),
211            ));
212        },
213    };
214    Ok(s)
215}
216
217fn bytify_implementation_float<O: ByteOrder>(negative: bool, float: LitFloat, output: &mut Vec<u8>) -> Result<(), Error> {
218    let num_bits = float.value();
219    let num_bits_suffix = float_to_suffix(negative, &float)?;
220    match num_bits_suffix {
221        FloatSuffix::F32 => {
222            if negative {
223                output.write_f32::<O>(-(num_bits as f32))?;
224            } else {
225                output.write_f32::<O>(  num_bits as f32 )?;
226            }
227        },
228        FloatSuffix::F64 => {
229            if negative {
230                output.write_f64::<O>(-num_bits)?;
231            } else {
232                output.write_f64::<O>( num_bits)?;
233            }
234        },
235        // Everything else is either invalid or ambiguous.
236        s => {
237            return Err(Error::UnsupportedNumberSuffix(format!("{:?}", s)));
238        },
239    }
240    Ok(())
241}
242
243fn bytify_implementation_element<O: ByteOrder>(lit: Lit, output: &mut Vec<u8>) -> Result<(), Error> {
244    match lit {
245        Lit::Char(c) => {
246            let offset = output.len();
247            output.resize(c.value().len_utf8() + offset, 0u8);
248            c.value().encode_utf8(&mut output[offset ..]);
249        },
250        Lit::Str(string) => {
251            output.extend_from_slice(string.value().as_bytes());
252        },
253        Lit::Int(int) => {
254            bytify_implementation_int::<O>(false, int, output)?;
255        },
256        Lit::Float(float) => {
257            bytify_implementation_float::<O>(false, float, output)?;
258        },
259        lit => {
260            return Err(Error::unsupported_lit(lit));
261        },
262    }
263    Ok(())
264}
265
266#[derive(Debug)]
267struct MyMacroInput {
268    list: Punctuated<Expr, Token![,]>,
269}
270
271impl Parse for MyMacroInput {
272
273    fn parse(input: ParseStream) -> Result<Self, SynError> {
274        Ok(MyMacroInput {
275            list: input.parse_terminated(Expr::parse)?,
276        })
277    }
278}
279
280fn bytify_implementation(input: MyMacroInput) -> Result<TokenStream, Error> {
281    let mut output: Vec<u8> = Vec::new();
282    for expr in input.list {
283        let (
284            endianness,
285            expr,
286        ) = match expr {
287            /* it is not, actually! */ Expr::Type(tpe_expr) => {
288                let expr = *tpe_expr.expr;
289                let endianness = match tpe_expr.ty.into_token_stream().to_string().as_str() {
290                    "BE" | "be" => Endianness::BE,
291                    "LE" | "le" => Endianness::LE,
292                    invalid => {
293                        return Err(Error::InvalidEndianness(invalid.to_string()));
294                    },
295                };
296                (endianness, expr)
297            },
298            expr => {
299                (DEFAULT_ENDIANNESS, expr)
300            },
301        };
302        match expr {
303            Expr::Lit(lit_expr) => {
304                if endianness == Endianness::BE {
305                    bytify_implementation_element::<BE>(lit_expr.lit, &mut output)?;
306                } else {
307                    bytify_implementation_element::<LE>(lit_expr.lit, &mut output)?;
308                }
309            },
310            Expr::Unary(unary_expr) => {
311                match unary_expr.op {
312                    UnOp::Neg(op) => {
313                        match *unary_expr.expr {
314                            Expr::Lit(lit_expr) => {
315                                match lit_expr.lit {
316                                    Lit::Int(int) => {
317                                        if endianness == Endianness::BE {
318                                            bytify_implementation_int::<BE>(true, int, &mut output)?;
319                                        } else {
320                                            bytify_implementation_int::<LE>(true, int, &mut output)?;
321                                        }
322                                    },
323                                    Lit::Float(float) => {
324                                        if endianness == Endianness::BE {
325                                            bytify_implementation_float::<BE>(true, float, &mut output)?;
326                                        } else {
327                                            bytify_implementation_float::<LE>(true, float, &mut output)?;
328                                        }
329                                    },
330                                    lit => {
331                                        return Err(Error::unsupported_lit(lit));
332                                    },
333                                }
334                            },
335                            expr => {
336                                return Err(Error::unsupported_prefixed_expression(UnOp::Neg(op), expr));
337                            },
338                        }
339                    },
340                    op => {
341                        return Err(Error::unsupported_prefixed_expression(op, *unary_expr.expr));
342                    },
343                }
344            },
345            expr => {
346                return Err(Error::unsupported_expression(expr));
347            },
348        }
349    }
350    Ok(quote! {
351        [
352            #(#output),*
353        ]
354    }.into())
355}
356
357#[proc_macro_hack]
358pub fn bytify(input: TokenStream) -> TokenStream {
359    let input = parse_macro_input!(input as MyMacroInput);
360    bytify_implementation(input).unwrap_or_else(|err| panic!("{}", err))
361}