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
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
//! Fast and easy bitfield proc macro
//! 
//! Provides a proc macro for compressing a data structure with data which can be expressed with bit 
//! lengths that are not a power of Two.
//! # Derive Generated Functions:
//! - Conversion between a sized u8 array and the rust structure you define.
//! - Peek and Set functions that allow the field to be accessed or overwritten within a sized u8 array.
//! 
//! For example we can define a data structure with 5 total bytes as:
//! - a field named one will be the first 3 bits.
//! - a field named two will be the next 19 bits.
//! - a field named six will be the next 14 bits.
//! - a field named four will be the next 4 bits.
//! 
//! 
//! ```
//! // Users code
//! use bondrewd::Bitfields;
//! #[derive(Bitfields)]
//! #[bondrewd(default_endianness = "be")]
//! struct SimpleExample {
//!     #[bondrewd(bit_length = 3)]
//!     one: u8,
//!     #[bondrewd(bit_length = 19)]
//!     two: u32,
//!     #[bondrewd(bit_length = 14)]
//!     six: u16,
//!     #[bondrewd(bit_length = 4)]
//!     four: u8,
//! }
//! ```
//! ```compile_fail
//! // Generated Code
//! impl Bitfields<5usize> for SimpleExample {
//!     const BIT_SIZE: usize = 40usize;
//!     fn into_bytes(self) -> [u8; 5usize] { .. }
//!     fn from_bytes([u8; 5usize]) -> Self { .. }
//! }
//! impl SimpleExample {
//!     pub fn peek_one(&[u8; 5usize]) -> u8 { .. }
//!     pub fn peek_two(&[u8; 5usize]) -> u32 { .. }
//!     pub fn peek_six(&[u8; 5usize]) -> u16 { .. }
//!     pub fn peek_four(&[u8; 5usize]) -> u8 { .. }
//!     pub fn set_one(&mut [u8; 5usize], u8) { .. }
//!     pub fn set_two(&mut [u8; 5usize], u32) { .. }
//!     pub fn set_six(&mut [u8; 5usize], u16) { .. }
//!     pub fn set_four(&mut [u8; 5usize], u8) { .. }
//! }
//! ```
//! # Supported Field Types
//! * All primitives other than usize and isize (i believe ambiguous sizing is bad for this type of work).
//! * Enums which implement the BitfieldEnum trait in bondrewd.
//! * Structs which implement the Bitfield trait in bondrewd.
//! 
//! # Struct Attributes
//! * `default_endianness = {"le" or "be"}` describes a default endianness for primitive fields.
//! * `read_from = {"msb0" or "lsb0"}` defines bit positioning. which end of the byte array to start at.
//! * `enforce_bytes = {BYTES}` defines a required resulting BIT_SIZE divided by 8 of the structure in condensed form.
//! * `enforce_bits = {BYTES}` defines a required resulting BIT_SIZE of the structure in condensed form.
//! * `enforce_full_bytes` defines that the resulting BIT_SIZE is required to be a multiple of 8.
//! * `reverse` defines that the entire byte array should be reversed before reading. no runtime cost.
//! 
//! # Field Attributes 
//! * `bit_length = {BITS}` define the total amount of bits to use when condensed.
//! * `byte_length = {BYTES}` define the total amount of bytes to use when condensed.
//! * `endianness = {"le" or "be"}` define per field endianess.
//! * `element_bit_length = {BITS}` describes a bit length for each element of an array.
//! * `element_byte_length = {BYTES}` describes a byte length for each element of an array.
//! * `block_bit_length = {BITS}` describes a bit length for the entire array dropping lower indexes first.
//! * `block_byte_length = {BYTES}` describes a byte length for the entire array dropping lower indexes first.
//! * `enum_primitive = "u8"` defines the size of the enum. the BitfieldEnum currently only supports u8.
//! * `struct_size = {SIZE}` defines the field as a struct which implements the Bitfield trait and the BYTE_SIZE const defined in said trait.
//! * `reserve` defines that this field should be ignored in from and into bytes functions.
//! * /!Untested!\ `bits = "RANGE"` - define the bit indexes yourself rather than let the proc macro figure 
//! it out. using a rust range in quotes.
//! 
//! ### Full Example Generated code
//! ```
//! use bondrewd::Bitfields;
//! struct SimpleFull {
//!     one: u8,
//!     two: u32,
//!     six: u16,
//!     four: u8,
//! }
//! impl Bitfields<5usize> for SimpleFull {
//!     const BIT_SIZE: usize = 40usize;
//!     fn into_bytes(self) -> [u8; 5usize] {
//!         let mut output_byte_buffer: [u8; 5usize] = [0u8; 5usize];
//!         output_byte_buffer[0usize] |= ((self.one as u8) << 5usize) & 224u8;
//!         let two_bytes = (self.two.rotate_left(2u32)).to_be_bytes();
//!         output_byte_buffer[0usize] |= two_bytes[1usize] & 31u8;
//!         output_byte_buffer[1usize] |= two_bytes[2usize];
//!         output_byte_buffer[2usize] |= two_bytes[3usize] & 252u8;
//!         let six_bytes = (self.six.rotate_right(4u32)).to_be_bytes();
//!         output_byte_buffer[2usize] |= six_bytes[0usize] & 3u8;
//!         output_byte_buffer[3usize] |= six_bytes[1usize];
//!         output_byte_buffer[4usize] |= six_bytes[0] & 240u8;
//!         output_byte_buffer[4usize] |= ((self.four as u8) << 0usize) & 15u8;
//!         output_byte_buffer
//!     }
//!     fn from_bytes(mut input_byte_buffer: [u8; 5usize]) -> Self {
//!         let one = Self::peek_one(&input_byte_buffer);
//!         let two = Self::peek_two(&input_byte_buffer);
//!         let six = Self::peek_six(&input_byte_buffer);
//!         let four = Self::peek_four(&input_byte_buffer);
//!         Self {
//!             one,
//!             two,
//!             six,
//!             four,
//!         }
//!     }
//! }
//! impl SimpleFull {
//!     #[inline]
//!     pub fn peek_one(input_byte_buffer: &[u8; 5usize]) -> u8 {
//!         ((input_byte_buffer[0usize] & 224u8) >> 5usize) as u8
//!     }
//!     #[inline]
//!     pub fn peek_two(input_byte_buffer: &[u8; 5usize]) -> u32 {
//!         u32::from_be_bytes({
//!             let mut two_bytes: [u8; 4usize] = [0u8; 4usize];
//!             two_bytes[1usize] = input_byte_buffer[0usize] & 31u8;
//!             two_bytes[2usize] |= input_byte_buffer[1usize];
//!             two_bytes[3usize] |= input_byte_buffer[2usize] & 252u8;
//!             two_bytes
//!         })
//!         .rotate_right(2u32)
//!     }
//!     #[inline]
//!     pub fn peek_six(input_byte_buffer: &[u8; 5usize]) -> u16 {
//!         u16::from_be_bytes({
//!             let mut six_bytes: [u8; 2usize] = [0u8; 2usize];
//!             six_bytes[0usize] = input_byte_buffer[2usize] & 3u8;
//!             six_bytes[1usize] |= input_byte_buffer[3usize];
//!             six_bytes[0] |= input_byte_buffer[4usize] & 240u8;
//!             six_bytes
//!         })
//!         .rotate_left(4u32)
//!     }
//!     #[inline]
//!     pub fn peek_four(input_byte_buffer: &[u8; 5usize]) -> u8 {
//!         ((input_byte_buffer[4usize] & 15u8) >> 0usize) as u8
//!     }
//!     #[inline]
//!     pub fn set_one(output_byte_buffer: &mut [u8; 5usize], one: u8) {
//!         output_byte_buffer[0usize] &= 31u8;
//!         output_byte_buffer[0usize] |= ((one as u8) << 5usize) & 224u8;
//!     }
//!     #[inline]
//!     pub fn set_two(output_byte_buffer: &mut [u8; 5usize], two: u32) {
//!         output_byte_buffer[0usize] &= 224u8;
//!         output_byte_buffer[2usize] &= 3u8;
//!         let two_bytes = (two.rotate_left(2u32)).to_be_bytes();
//!         output_byte_buffer[0usize] |= two_bytes[1usize] & 31u8;
//!         output_byte_buffer[1usize] |= two_bytes[2usize];
//!         output_byte_buffer[2usize] |= two_bytes[3usize] & 252u8;
//!     }
//!     #[inline]
//!     pub fn set_six(output_byte_buffer: &mut [u8; 5usize], six: u16) {
//!         output_byte_buffer[2usize] &= 252u8;
//!         output_byte_buffer[3usize] = 0u8;
//!         output_byte_buffer[4usize] &= 15u8;
//!         let six_bytes = (six.rotate_right(4u32)).to_be_bytes();
//!         output_byte_buffer[2usize] |= six_bytes[0usize] & 3u8;
//!         output_byte_buffer[3usize] |= six_bytes[1usize];
//!         output_byte_buffer[4usize] |= six_bytes[0] & 240u8;
//!     }
//!     #[inline]
//!     pub fn set_four(output_byte_buffer: &mut [u8; 5usize], four: u8) {
//!         output_byte_buffer[4usize] &= 240u8;
//!         output_byte_buffer[4usize] |= ((four as u8) << 0usize) & 15u8;
//!     }
//! }
//! ```
extern crate proc_macro;
mod enums;
use enums::parse::EnumInfo;
mod structs;
use structs::common::StructInfo;
use structs::from_bytes::create_from_bytes_field_quotes;
use structs::into_bytes::create_into_bytes_field_quotes;

use proc_macro::TokenStream;
use quote::{format_ident, quote};
use syn::{parse_macro_input, DeriveInput};





/// Generates an implementation of the bondrewd::Bitfield trait, as well as peek and set functions for direct
/// sized u8 arrays access.
/// 
/// # Struct Derive Tasks
/// - [x] read_direction ( the bit order is reversed with no runtime cost)
/// - [x] flip (flip the entire byte order with no runtime cost)
/// - [x] Little Endian primitives
///     - [x] Impl into_bytes.
///     - [x] Impl peek_{field} and peek_slice_{field} functions.
///     - [x] Impl from_bytes.
/// - [x] Big Endian primitives
///     - [x] Impl into_bytes.
///     - [x] Impl peek_{field} and peek_slice_{field} functions.
///     - [x] Impl from_bytes.
/// - [x] Struct
///     - [x] Impl into_bytes.
///     - [x] Impl peek_{field} and peek_slice_{field} functions.
///     - [x] Impl from_bytes.
/// - [x] Enum
///     - [x] Impl into_bytes.
///     - [x] Impl peek_{field} and peek_slice_{field} functions.
///     - [x] Impl from_bytes.
/// - [x] Element Arrays
///     - [x] Impl into_bytes.
///     - [x] Impl peek_{field} and peek_slice_{field} functions.
///     - [x] Impl from_bytes.
/// - [x] Block Arrays
///     - [x] Impl into_bytes.
///     - [x] Impl peek_{field} and peek_slice_{field} functions.
///     - [x] Impl from_bytes.
/// - [x] bit size enforcement as an option to ensure proper struct sizing
///     - [x] full bytes attribute (BIT_SIZE % 8 == 0)
///     - [x] total bit/bytes length enforcement by a specified amount of
///             bits or bytes.
/// - [ ] make single byte primitives automatically use big endianness if not defined.
/// * primitives should exclude usize and isize due to ambiguous sizing
#[proc_macro_derive(
    Bitfields,
    attributes(
        bondrewd,
    )
)]
pub fn derive_smart_fields(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    // parse the input into a StructInfo which contains all the information we
    // along with some helpful structures to generate our Bitfield code.
    let struct_info = match StructInfo::parse(&input) {
        Ok(parsed_struct) => parsed_struct,
        Err(err) => {
            return TokenStream::from(err.to_compile_error());
        }
    };
    // get the struct size and name so we can use them in a quote.
    let struct_size = struct_info.total_bytes();
    let struct_name = format_ident!("{}", struct_info.name);

    
    // get a list of all fields from_bytes logic which gets there bytes from an array called
    // input_byte_buffer.
    let slice_fns: bool;
    #[cfg(not(feature = "slice_fns"))]
    {
        slice_fns = false;
    }
    #[cfg(feature = "slice_fns")]
    {
        slice_fns = true;
    }
    // get a list of all fields into_bytes logic which puts there bytes into an array called
    // output_byte_buffer.
    let fields_into_bytes = match create_into_bytes_field_quotes(&struct_info, slice_fns) {
        Ok(ftb) => ftb,
        Err(err) => return TokenStream::from(err.to_compile_error()),
    };
    let fields_from_bytes = match create_from_bytes_field_quotes(&struct_info, slice_fns) {
        Ok(ffb) => ffb,
        Err(err) => return TokenStream::from(err.to_compile_error()),
    };
    // combine all of the into_bytes quotes separated by newlines
    let into_bytes_quote = fields_into_bytes.into_bytes_fn;
    let mut set_quotes = fields_into_bytes.set_field_fns;

    if let Some(set_slice_quote) = fields_into_bytes.set_slice_field_fns {
        set_quotes = quote! {
            #set_quotes
            #set_slice_quote
        }
    }

    let from_bytes_quote = fields_from_bytes.from_bytes_fn;
    let mut peek_quotes = fields_from_bytes.peek_field_fns;

    if let Some(peek_slice_quote) = fields_from_bytes.peek_slice_field_fns {
        peek_quotes = quote! {
            #peek_quotes
            #peek_slice_quote
        }
    }

    let getter_setters_quotes = quote! {
        impl #struct_name {
            #peek_quotes
            #set_quotes
        }
    };

    // get the bit size of the entire set of fields to fill in trait requirement.
    let bit_size = struct_info.total_bits();

    // put it all together.
    // to_bytes_quote will put all of the fields in self into a array called output_byte_buffer.
    // so for into_bytes all we need is the fn declaration, the output_byte_buffer, and to return
    // that buffer.
    // from_bytes is essentially the same minus a variable because input_byte_buffer is the input.
    // slap peek quotes inside a impl block at the end and we good to go
    let to_bytes_quote = quote! {
        impl Bitfields<#struct_size> for #struct_name {
            const BIT_SIZE: usize = #bit_size;
            #into_bytes_quote
            #from_bytes_quote
        }
        #getter_setters_quotes
    };

    TokenStream::from(to_bytes_quote)
}

/// Generates an implementation of bondrewd::BitfieldEnum trait.
/// 
/// # Enum Derive Tasks
/// - [x] from_primitive.
/// - [x] into_primitive.
/// - [x] Invalid flag (Invalid values will be dropped an a generic no field
///                         variant will be used).
/// - [x] Invalid catch (stores the actual primitive in a 1 field Variant).
/// - [ ] types other than u8.
#[proc_macro_derive(BitfieldEnum, attributes(bondrewd_enum))]
pub fn derive_bondrewd_enum(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    let enum_info = match EnumInfo::parse(&input) {
        Ok(parsed_enum) => parsed_enum,
        Err(err) => {
            return TokenStream::from(err.to_compile_error());
        }
    };
    let into = enums::into_bytes::generate_into_bytes(&enum_info);
    let from = enums::from_bytes::generate_from_bytes(&enum_info);
    let enum_name = enum_info.name;
    let primitive = enum_info.primitive;
    TokenStream::from(quote! {
        impl bondrewd::BitfieldEnum for #enum_name {
            type Primitive = #primitive;
            #into
            #from
        }
    })
}