1#[macro_export]
77macro_rules! binary_layout {
78 ($name: ident, $endianness: ident, {$($field_name: ident : $field_type: ty $(as $underlying_type: ty)?),* $(,)?}) => {
79 $crate::internal::doc_comment!{
80 concat!{"
81 This module is autogenerated. It defines a layout using the [binary_layout] crate based on the following definition:
82 ```ignore
83 binary_layout!(", stringify!($name), ", ", stringify!($endianness), ", {", $("
84 ", stringify!($field_name), ": ", stringify!($field_type), $(" as ", stringify!($underlying_type), )? ",", )* "
85 });
86 ```
87 "},
88 #[allow(dead_code)]
89 pub mod $name {
90 #[allow(unused_imports)]
91 use super::*;
92
93 $crate::binary_layout!(@impl_fields $crate::$endianness, Some(0), {$($field_name : $field_type $(as $underlying_type)?),*});
94
95 $crate::internal::doc_comment!{
96 concat!{"
97 The [View] struct defines the [FieldView](crate::FieldView) API.
98 An instance of [View] wraps a storage (either borrowed or owned)
99 and allows accessors for the layout fields.
100
101 This view is based on the following layout definition:
102 ```ignore
103 binary_layout!(", stringify!($name), ", ", stringify!($endianness), ", {", $("
104 ", stringify!($field_name), ": ", stringify!($field_type), $(" as ", stringify!($underlying_type), )? ",",)* "
105 });
106 ```
107 "},
108 pub struct View<S: AsRef<[u8]>> {
109 storage: S,
110 }
111 }
112 impl <S: AsRef<[u8]>> View<S> {
113 #[inline]
120 pub fn new(storage: S) -> Self {
121 Self {storage}
122 }
123
124 #[inline]
128 pub fn into_storage(self) -> S {
129 self.storage
130 }
131
132 $crate::binary_layout!(@impl_view_into {$($field_name),*});
133 }
134 impl <S: AsRef<[u8]>> View<S> {
135 $crate::binary_layout!(@impl_view_asref {$($field_name),*});
136 }
137 impl <S: AsRef<[u8]> + AsMut<[u8]>> View<S> {
138 $crate::binary_layout!(@impl_view_asmut {$($field_name),*});
139 }
140
141 pub struct NestedView;
160 impl <S: AsRef<[u8]>> $crate::internal::OwningNestedView<$crate::Data<S>> for NestedView where S: AsRef<[u8]> {
161 type View = View<$crate::Data<S>>;
162
163 #[inline(always)]
164 fn into_view(storage: $crate::Data<S>) -> Self::View {
165 Self::View {storage}
166 }
167 }
168 impl <S: AsRef<[u8]>> $crate::internal::BorrowingNestedView<S> for NestedView {
169 type View = View<S>;
170
171 #[inline(always)]
172 fn view(storage: S) -> Self::View {
173 Self::View {storage: storage.into()}
174 }
175 }
176
177 impl $crate::internal::NestedViewInfo for NestedView {
178 const SIZE: Option<usize> = SIZE;
179 }
180 }
181 }
182 };
183
184 (@impl_fields $endianness: ty, $offset_accumulator: expr, {}) => {
185 pub const SIZE: Option<usize> = $offset_accumulator;
188 };
189 (@impl_fields $endianness: ty, $offset_accumulator: expr, {$name: ident : $type: ty as $underlying_type: ty $(, $($tail:tt)*)?}) => {
190 $crate::internal::doc_comment!{
191 concat!("Metadata and [Field](crate::Field) API accessors for the `", stringify!($name), "` field"),
192 #[allow(non_camel_case_types)]
193 pub type $name = $crate::WrappedField::<$underlying_type, $type, $crate::PrimitiveField::<$underlying_type, $endianness, {$crate::internal::unwrap_field_size($offset_accumulator)}>>;
194 }
195 $crate::binary_layout!(@impl_fields $endianness, ($crate::internal::option_usize_add(<$name as $crate::Field>::OFFSET, <$name as $crate::Field>::SIZE)), {$($($tail)*)?});
196 };
197 (@impl_fields $endianness: ty, $offset_accumulator: expr, {$name: ident : $type: ty $(, $($tail:tt)*)?}) => {
198 $crate::internal::doc_comment!{
199 concat!("Metadata and [Field](crate::Field) API accessors for the `", stringify!($name), "` field"),
200 #[allow(non_camel_case_types)]
201 pub type $name = $crate::PrimitiveField::<$type, $endianness, {$crate::internal::unwrap_field_size($offset_accumulator)}>;
202 }
203 $crate::binary_layout!(@impl_fields $endianness, ($crate::internal::option_usize_add(<$name as $crate::Field>::OFFSET, <$name as $crate::Field>::SIZE)), {$($($tail)*)?});
204 };
205
206 (@impl_view_asref {}) => {};
207 (@impl_view_asref {$name: ident $(, $name_tail: ident)*}) => {
208 $crate::internal::doc_comment!{
209 concat!("Return a [FieldView](crate::FieldView) with read access to the `", stringify!($name), "` field"),
210 #[inline]
211 pub fn $name(&self) -> <$name as $crate::internal::StorageToFieldView<&[u8]>>::View {
212 <$name as $crate::internal::StorageToFieldView<&[u8]>>::view(self.storage.as_ref())
213 }
214 }
215 $crate::binary_layout!(@impl_view_asref {$($name_tail),*});
216 };
217
218 (@impl_view_asmut {}) => {};
219 (@impl_view_asmut {$name: ident $(, $name_tail: ident)*}) => {
220 $crate::internal::paste!{
221 $crate::internal::doc_comment!{
222 concat!("Return a [FieldView](crate::FieldView) with write access to the `", stringify!($name), "` field"),
223 #[inline]
224 pub fn [<$name _mut>](&mut self) -> <$name as $crate::internal::StorageToFieldView<&mut [u8]>>::View {
225 <$name as $crate::internal::StorageToFieldView<&mut [u8]>>::view(self.storage.as_mut())
226 }
227 }
228 }
229 $crate::binary_layout!(@impl_view_asmut {$($name_tail),*});
230 };
231
232 (@impl_view_into {}) => {};
233 (@impl_view_into {$name: ident $(, $name_tail: ident)*}) => {
234 $crate::internal::paste!{
235 $crate::internal::doc_comment!{
236 concat!("Destroy the [View] and return a field accessor to the `", stringify!($name), "` field owning the storage. This is mostly useful for [FieldView::extract](crate::FieldView::extract)"),
237 #[inline]
238 pub fn [<into_ $name>](self) -> <$name as $crate::internal::StorageIntoFieldView<S>>::View {
239 <$name as $crate::internal::StorageIntoFieldView<S>>::into_view(self.storage)
240 }
241 }
242 }
243 $crate::binary_layout!(@impl_view_into {$($name_tail),*});
244 };
245}
246
247#[deprecated = "The `define_layout!` macro was renamed to `binary_layout!` and the old name will be removed in future versions."]
249#[macro_export]
250macro_rules! define_layout {
251 ($name: ident, $endianness: ident, {$($field_name: ident : $field_type: ty $(as $underlying_type: ty)?),* $(,)?}) => {
252 $crate::binary_layout!($name, $endianness, {$($field_name : $field_type $(as $underlying_type)?),*});
253 }
254}
255
256#[inline(always)]
260pub const fn unwrap_field_size(opt: Option<usize>) -> usize {
261 match opt {
262 Some(x) => x,
263 None => {
264 panic!("Error: Fields without a static size (e.g. open-ended byte arrays) can only be used at the end of a layout");
265 }
266 }
267}
268
269#[inline(always)]
271pub const fn option_usize_add(lhs: usize, rhs: Option<usize>) -> Option<usize> {
272 match (lhs, rhs) {
273 (lhs, Some(rhs)) => Some(lhs + rhs),
274 (_, None) => None,
275 }
276}
277
278#[cfg(test)]
279mod tests {
280 use crate::prelude::*;
281
282 use rand::{rngs::StdRng, RngCore, SeedableRng};
283
284 #[cfg(feature = "std")]
285 fn data_region_vec(size: usize, seed: u64) -> Vec<u8> {
286 let mut rng = StdRng::seed_from_u64(seed);
287 let mut res = vec![0; size];
288 rng.fill_bytes(&mut res);
289 res
290 }
291
292 fn data_region(seed: u64) -> [u8; 1024] {
293 let mut rng = StdRng::seed_from_u64(seed);
294 let mut res = [0; 1024];
295 rng.fill_bytes(&mut res);
296 res
297 }
298
299 binary_layout!(module_level_layout, LittleEndian, {
300 first: i8,
301 second: i64,
302 third: u16,
303 });
304
305 #[test]
306 fn layouts_can_be_defined_at_module_level() {
307 let storage: [u8; 1024] = [0; 1024];
308 let view = module_level_layout::View::new(storage);
309 assert_eq!(0, view.third().read());
310 }
311
312 #[test]
313 fn layouts_can_be_defined_at_function_level() {
314 binary_layout!(function_level_layout, LittleEndian, {
315 first: i8,
316 second: i64,
317 third: u16,
318 });
319
320 let storage: [u8; 1024] = [0; 1024];
321 let view = function_level_layout::View::new(storage);
322 assert_eq!(0, view.third().read());
323 }
324
325 #[test]
326 fn can_be_created_with_and_without_trailing_comma() {
327 binary_layout!(first, LittleEndian, { field: u8 });
328 binary_layout!(second, LittleEndian, {
329 field: u8,
330 second: u16
331 });
332 binary_layout!(third, LittleEndian, {
333 field: u8,
334 });
335 binary_layout!(fourth, LittleEndian, {
336 field: u8,
337 second: u16,
338 });
339 }
340
341 #[cfg(feature = "std")]
342 #[test]
343 fn there_can_be_multiple_views_if_readonly_vec() {
344 binary_layout!(my_layout, BigEndian, {
345 field1: u16,
346 field2: i64,
347 });
348
349 let storage = data_region_vec(1024, 0);
350 let view1 = my_layout::View::new(&storage);
351 let view2 = my_layout::View::new(&storage);
352 view1.field1().read();
353 view2.field1().read();
354 }
355
356 #[test]
357 fn there_can_be_multiple_views_if_readonly_array() {
358 binary_layout!(my_layout, BigEndian, {
359 field1: u16,
360 field2: i64,
361 });
362
363 let storage = data_region(0);
364 let view1 = my_layout::View::new(&storage);
365 let view2 = my_layout::View::new(&storage);
366 view1.field1().read();
367 view2.field1().read();
368 }
369
370 #[test]
371 fn size_of_sized_layout() {
372 binary_layout!(my_layout, LittleEndian, {
373 field1: u16,
374 field2: i64,
375 });
376 assert_eq!(Some(10), my_layout::SIZE);
377 }
378
379 #[test]
380 fn size_of_unsized_layout() {
381 binary_layout!(my_layout, LittleEndian, {
382 field: u16,
383 tail: [u8],
384 });
385 assert_eq!(None, my_layout::SIZE);
386 }
387}