forked from winstonallo/bitstruct
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlib.rs
More file actions
201 lines (187 loc) · 5.45 KB
/
Copy pathlib.rs
File metadata and controls
201 lines (187 loc) · 5.45 KB
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
use proc_macro::TokenStream;
use syn::parse_macro_input;
use syn::DeriveInput;
mod generator;
mod parser;
/// Generates a packed bitmap newtype struct with field-level bit access.
///
/// The macro expands to a newtype struct around a `u8` to `u128`, depending on the total bit width
/// of the definition, with automatically generated getters and setters for each field.
///
/// ### API
/// #### Usage Example
/// ```
/// use macros::bitmap;
///
/// bitmap!(
/// struct Player {
/// imposter: u1,
/// finished_tasks: u3,
/// kills: u3,
/// }
/// );
///
/// let mut player = Player(0);
/// assert_eq!(std::mem::size_of::<Player>(), 1);
///
/// player.set_imposter(1);
/// player.set_finished_tasks(5);
/// player.set_kills(3);
///
/// assert_eq!(player.imposter(), 1);
/// assert_eq!(player.finished_tasks(), 5);
/// assert_eq!(player.kills(), 3);
/// assert_eq!(*player, 0b01101011);
/// ```
/// #### Accessing fields
/// For each field `name: T`, where `T` is the smallest possible integer such that
/// `field_size <= integer.size`, `bitmap!` generates:
///
/// - `fn name(&self) -> T` — returns the value for `name`
/// - `fn set_name(&mut self, val: T)` — sets the value for `name`
///
/// #### Accessing the raw value
/// For the struct `Bits(T)`, where `T` is the unsigned integer type used for storage,
/// the following traits are implemented:
/// - `From<Bits> for T`
/// - `Deref for Bits`, with `fn deref(&self) -> T`
///
/// ```
/// use macros::bitmap;
///
/// bitmap!(
/// struct Bits {
/// a: u32,
/// b: u16,
/// c: u16,
/// }
/// );
///
/// let bits = Bits(0);
/// let underlying_u64: u64 = bits.into();
/// let underlying_u64 = *bits;
/// ```
/// ### Supported field types:
/// ```
/// use macros::bitmap;
///
/// bitmap!(
/// struct Bits {
/// flag: u1,
/// counter: u7,
/// }
/// );
/// ```
/// Each field must be in the form `uN`, where `1 <= N <= 128`.
/// ### Maximum total size
/// `bitmap!` uses the smallest possible integer type such that `total_bit_width <= integer.bit_width`.
/// The total bit width must fit into a `u128`. If you need more than that, consider using a `Vec`
/// of `bitmap`s.
/// ### Storage order
/// Fields are packed from **most significant bit (MSB)** to **least significant bit (LSB)**, matching
/// big-endian order.
///
/// This means the first declared field is stored in the highest bits of the underlying storage integer.
/// ```
/// use macros::bitmap;
///
/// bitmap!(
/// struct Bits {
/// a: u8,
/// b: u8,
/// }
/// );
///
/// let mut bits = Bits(0);
/// bits.set_a(0xaa)
/// .set_b(0xbb);
///
/// assert_eq!(*bits, 0xaabb);
/// ```
///
/// ### Note
/// `bitmap!` is built with hardware configuration in mind, where most packed bitmaps have a size
/// aligned to integer sizes. It does not use the _smallest possible size_: a bitmap with only one `u33`
/// field will take up 64 bits of space.
/// ```
/// use macros::bitmap;
///
/// bitmap!(
/// struct Bits {
/// field: u33,
/// }
/// );
///
/// assert_eq!(core::mem::size_of::<Bits>(), 8);
/// ```
///
#[proc_macro]
pub fn bitmap(input: TokenStream) -> TokenStream {
let parsed = parse_macro_input!(input as parser::BitmapInput);
match generator::expand_bitmap(parsed) {
Ok(tokens) => tokens.into(),
Err(err) => err.to_compile_error().into(),
}
}
/// Generates a packed bitmap newtype struct with field-level bit access.
///
/// This attribute macro works exactly like the `bitmap!` macro but uses attribute syntax.
///
/// ### Usage Example
/// ```
/// use bitmap::bitmap_attr;
///
/// #[bitmap_attr]
/// struct Player {
/// imposter: u1,
/// finished_tasks: u3,
/// kills: u3,
/// }
///
/// let mut player = Player(0);
/// assert_eq!(std::mem::size_of::<Player>(), 1);
///
/// player.set_imposter(1);
/// player.set_finished_tasks(5);
/// player.set_kills(3);
///
/// assert_eq!(player.imposter(), 1);
/// assert_eq!(player.finished_tasks(), 5);
/// assert_eq!(player.kills(), 3);
/// assert_eq!(*player, 0b01101011);
/// ```
#[proc_macro_attribute]
pub fn bitmap_attr(_args: TokenStream, input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let bitmap_input = convert_derive_to_bitmap_input(input);
match generator::expand_bitmap(bitmap_input) {
Ok(tokens) => tokens.into(),
Err(err) => err.to_compile_error().into(),
}
}
fn convert_derive_to_bitmap_input(input: DeriveInput) -> parser::BitmapInput {
let name = input.ident;
let syn::Data::Struct(data_struct) = input.data else {
panic!("#[bitmap_attr] can only be used on structs");
};
let syn::Fields::Named(fields_named) = data_struct.fields else {
panic!("#[bitmap_attr] struct must have named fields");
};
let fields = fields_named.named.into_iter().map(|field| {
let field_name = field.ident.expect("Field must have a name");
let field_type = field.ty;
let syn::Type::Path(type_path) = field_type else {
panic!("Field type must be a path like u1, u7, etc.");
};
let segment = type_path.path.segments.last().expect("Type path must have a segment");
let size = parser::parse_bit_width(&segment.ident).expect("Invalid bit width");
parser::FieldDef {
name: field_name,
size,
}
}).collect();
parser::BitmapInput {
name,
fields,
}
}