binary_layout/fields/
wrapped.rs

1use core::convert::Infallible;
2use core::fmt::Debug;
3use core::marker::PhantomData;
4#[cfg(feature = "std")]
5use thiserror::Error;
6
7use crate::utils::infallible::IsInfallible;
8
9use super::{
10    primitive::{FieldCopyAccess, FieldView},
11    Field, StorageIntoFieldView, StorageToFieldView,
12};
13
14/// Implementing the [LayoutAs] trait for a custom type allows that custom type to be used
15/// as the type of a layout field. Note that the value of this type is copied each time it
16/// is accessed, so this is only recommended for primitive wrappers of primitive types,
17/// not for types that are expensive to copy.
18///
19/// # Example
20/// ```
21/// use binary_layout::{prelude::*, LayoutAs};
22/// use core::convert::Infallible;
23///
24/// struct MyIdType(u64);
25/// impl LayoutAs<u64> for MyIdType {
26///   type ReadError = Infallible;
27///   type WriteError = Infallible;
28///   fn try_read(v: u64) -> Result<MyIdType, Infallible> {
29///     Ok(MyIdType(v))
30///   }
31///
32///   fn try_write(v: MyIdType) -> Result<u64, Infallible> {
33///     Ok(v.0)
34///   }
35/// }
36///
37/// binary_layout!(my_layout, BigEndian, {
38///   // ... other fields ...
39///   field: MyIdType as u64,
40///   // ... other fields ...
41/// });
42///
43/// # fn main() {}
44/// ```
45pub trait LayoutAs<U>: Sized {
46    /// See [FieldCopyAccess::ReadError].
47    ///
48    /// If reading cannot fail, set this to [core::convert::Infallible] because that will make
49    /// [FieldReadExt::read](crate::FieldReadExt::read) available for the type. For any other
50    /// error type, you will have to use [FieldCopyAccess::try_read] when reading the field.
51    type ReadError;
52    /// See [FieldCopyAccess::WriteError].
53    ///
54    /// If writing cannot fail, set this to [core::convert::Infallible] because that will make
55    /// [FieldWriteExt::write](crate::FieldWriteExt::write) available for the type. For any other
56    /// error type, you will have to use [FieldCopyAccess::try_write] when writing the field.
57    type WriteError;
58
59    /// Implement this to define how the custom type is constructed from the underlying type
60    /// after it was read from a layouted binary slice.
61    fn try_read(v: U) -> Result<Self, Self::ReadError>;
62
63    /// Implement this to define how the custom type is converted into the underlying type
64    /// so it can be written into a layouted binary slice.
65    fn try_write(v: Self) -> Result<U, Self::WriteError>;
66}
67
68/// The error being thrown when reading or writing fields that use custom data types implemented via [LayoutAs].
69#[derive(Debug)]
70#[cfg_attr(feature = "std", derive(Error))]
71pub enum WrappedFieldError<PrimitiveAccessError, LayoutAsError> {
72    /// An error happened when reading or writing the primitive data type the [LayoutAs] stores values at.
73    #[cfg_attr(
74        feature = "std",
75        error("Error accessing (reading or writing) the primitive data type: {0}")
76    )]
77    PrimitiveAccessError(PrimitiveAccessError),
78    /// An error happened in the call to [LayoutAs::try_read] or [LayoutAs::try_write]
79    #[cfg_attr(
80        feature = "std",
81        error("Error mapping the primitive data type in `LayoutAs`: {0}")
82    )]
83    LayoutAsError(LayoutAsError),
84}
85
86impl IsInfallible for WrappedFieldError<Infallible, Infallible> {}
87
88/// A [WrappedField] is a [Field] that, unlike [PrimitiveField](crate::PrimitiveField), does not directly represent a primitive type.
89/// Instead, it represents a wrapper type that can be converted to/from a primitive type using the [LayoutAs] trait.
90/// See [Field] for more info on this API.
91///
92/// # Example (reading/writing cannot throw errors):
93/// ```
94/// use binary_layout::{prelude::*, LayoutAs};
95/// use core::convert::Infallible;
96///
97/// struct MyIdType(u64);
98/// impl LayoutAs<u64> for MyIdType {
99///   type ReadError = Infallible;
100///   type WriteError = Infallible;
101///
102///   fn try_read(v: u64) -> Result<MyIdType, Infallible> {
103///     Ok(MyIdType(v))
104///   }
105///
106///   fn try_write(v: MyIdType) -> Result<u64, Infallible> {
107///     Ok(v.0)
108///   }
109/// }
110///
111/// binary_layout!(my_layout, BigEndian, {
112///   // ... other fields ...
113///   field: MyIdType as u64,
114///   // ... other fields ...
115/// });
116///
117/// fn func(storage_data: &mut [u8]) {
118///   // read some data
119///   let read_data: MyIdType = my_layout::field::read(storage_data);
120///   // equivalent: let read_data = MyIdType(u16::from_le_bytes((&storage_data[0..2]).try_into().unwrap()));
121///
122///   // write some data
123///   my_layout::field::write(storage_data, MyIdType(10));
124///   // equivalent: data_slice[18..22].copy_from_slice(&10u32.to_le_bytes());
125/// }
126///
127/// # fn main() {
128/// #   let mut storage = [0; 1024];
129/// #   func(&mut storage);
130/// # }
131/// ```
132///
133/// # Example (reading/writing can throw errors):
134/// ```
135/// use binary_layout::{prelude::*, WrappedFieldError, LayoutAs};
136/// use core::convert::Infallible;
137///
138/// struct MyIdType(u64);
139/// impl LayoutAs<u64> for MyIdType {
140///   type ReadError = &'static str;
141///   type WriteError = &'static str;
142///
143///   fn try_read(v: u64) -> Result<MyIdType, &'static str> {
144///     Ok(MyIdType(v))
145///   }
146///
147///   fn try_write(v: MyIdType) -> Result<u64, &'static str> {
148///     Ok(v.0)
149///   }
150/// }
151///
152/// binary_layout!(my_layout, BigEndian, {
153///   // ... other fields ...
154///   field: MyIdType as u64,
155///   // ... other fields ...
156/// });
157///
158/// fn func(storage_data: &mut [u8]) -> Result<(), WrappedFieldError<Infallible, &'static str>> {
159///   // read some data
160///   let read_data: MyIdType = my_layout::field::try_read(storage_data)?;
161///   // equivalent: let read_data = MyIdType(u16::from_le_bytes((&storage_data[0..2]).try_into().unwrap()));
162///
163///   // write some data
164///   my_layout::field::try_write(storage_data, MyIdType(10))?;
165///   // equivalent: data_slice[18..22].copy_from_slice(&10u32.to_le_bytes());
166///
167///   Ok(())
168/// }
169///
170/// # fn main() {
171/// #   let mut storage = [0; 1024];
172/// #   func(&mut storage);
173/// # }
174/// ```
175pub struct WrappedField<U, T: LayoutAs<U>, F: Field> {
176    _p1: PhantomData<U>,
177    _p2: PhantomData<T>,
178    _p3: PhantomData<F>,
179}
180
181impl<U, T: LayoutAs<U>, F: Field> Field for WrappedField<U, T, F> {
182    /// See [Field::Endian]
183    type Endian = F::Endian;
184    /// See [Field::OFFSET]
185    const OFFSET: usize = F::OFFSET;
186    /// See [Field::SIZE]
187    const SIZE: Option<usize> = F::SIZE;
188}
189
190impl<
191        'a,
192        U,
193        T: LayoutAs<U>,
194        F: FieldCopyAccess<HighLevelType = U> + StorageToFieldView<&'a [u8]>,
195    > StorageToFieldView<&'a [u8]> for WrappedField<U, T, F>
196{
197    type View = FieldView<&'a [u8], Self>;
198
199    #[inline(always)]
200    fn view(storage: &'a [u8]) -> Self::View {
201        Self::View::new(storage)
202    }
203}
204
205impl<
206        'a,
207        U,
208        T: LayoutAs<U>,
209        F: FieldCopyAccess<HighLevelType = U> + StorageToFieldView<&'a mut [u8]>,
210    > StorageToFieldView<&'a mut [u8]> for WrappedField<U, T, F>
211{
212    type View = FieldView<&'a mut [u8], Self>;
213
214    #[inline(always)]
215    fn view(storage: &'a mut [u8]) -> Self::View {
216        Self::View::new(storage)
217    }
218}
219
220impl<
221        U,
222        S: AsRef<[u8]>,
223        T: LayoutAs<U>,
224        F: FieldCopyAccess<HighLevelType = U> + StorageIntoFieldView<S>,
225    > StorageIntoFieldView<S> for WrappedField<U, T, F>
226{
227    type View = FieldView<S, Self>;
228
229    #[inline(always)]
230    fn into_view(storage: S) -> Self::View {
231        Self::View::new(storage)
232    }
233}
234
235impl<U, T: LayoutAs<U>, F: FieldCopyAccess<HighLevelType = U>> FieldCopyAccess
236    for WrappedField<U, T, F>
237{
238    /// See [FieldCopyAccess::ReadError]
239    type ReadError = WrappedFieldError<F::ReadError, T::ReadError>;
240    /// See [FieldCopyAccess::WriteError]
241    type WriteError = WrappedFieldError<F::WriteError, T::WriteError>;
242    /// See [FieldCopyAccess::HighLevelType]
243    type HighLevelType = T;
244
245    /// Read the field from a given data region, assuming the defined layout, using the [Field] API.
246    ///
247    /// # Example:
248    /// ```
249    /// use binary_layout::{prelude::*, LayoutAs};
250    /// use core::convert::Infallible;
251    ///
252    /// #[derive(Debug, PartialEq, Eq)]
253    /// struct MyIdType(u64);
254    /// impl LayoutAs<u64> for MyIdType {
255    ///   type ReadError = Infallible;
256    ///   type WriteError = Infallible;
257    ///
258    ///   fn try_read(v: u64) -> Result<MyIdType, Infallible> {
259    ///     Ok(MyIdType(v))
260    ///   }
261    ///
262    ///   fn try_write(v: MyIdType) -> Result<u64, Infallible> {
263    ///     Ok(v.0)
264    ///   }
265    /// }
266    ///
267    /// binary_layout!(my_layout, LittleEndian, {
268    ///   //... other fields ...
269    ///   some_integer_field: MyIdType as u64,
270    ///   //... other fields ...
271    /// });
272    ///
273    /// fn func(storage_data: &mut [u8]) {
274    ///   my_layout::some_integer_field::write(storage_data, MyIdType(50));
275    ///   assert_eq!(MyIdType(50), my_layout::some_integer_field::read(storage_data));
276    /// }
277    ///
278    /// # fn main() {
279    /// #   let mut storage = [0; 1024];
280    /// #   func(&mut storage);
281    /// # }
282    /// ```
283    #[inline(always)]
284    fn try_read(storage: &[u8]) -> Result<Self::HighLevelType, Self::ReadError> {
285        let v = F::try_read(storage).map_err(WrappedFieldError::PrimitiveAccessError)?;
286        let value = <T as LayoutAs<U>>::try_read(v).map_err(WrappedFieldError::LayoutAsError)?;
287        Ok(value)
288    }
289
290    /// Write the field to a given data region, assuming the defined layout, using the [Field] API.
291    ///
292    /// # Example:
293    /// See [FieldCopyAccess::try_read] for an example
294    #[inline(always)]
295    fn try_write(storage: &mut [u8], v: Self::HighLevelType) -> Result<(), Self::WriteError> {
296        let v = <T as LayoutAs<U>>::try_write(v).map_err(WrappedFieldError::LayoutAsError)?;
297        F::try_write(storage, v).map_err(WrappedFieldError::PrimitiveAccessError)?;
298        Ok(())
299    }
300}
301
302#[cfg(test)]
303mod tests {
304    #![allow(clippy::float_cmp)]
305    use crate::prelude::*;
306    use crate::{LayoutAs, PrimitiveField, WrappedField};
307    use core::convert::{Infallible, TryInto};
308
309    #[derive(Debug, PartialEq, Eq)]
310    struct Wrapped<T>(T);
311    impl<T> LayoutAs<T> for Wrapped<T> {
312        type ReadError = Infallible;
313        type WriteError = Infallible;
314
315        fn try_read(v: T) -> Result<Self, Infallible> {
316            Ok(Self(v))
317        }
318        fn try_write(v: Self) -> Result<T, Infallible> {
319            Ok(v.0)
320        }
321    }
322
323    macro_rules! test_wrapped_field {
324        ($type:ty, $expected_size:expr, $value1:expr, $value2:expr) => {
325            test_wrapped_field!(@case, $type, $expected_size, $value1, $value2, little, LittleEndian, from_le_bytes);
326            test_wrapped_field!(@case, $type, $expected_size, $value1, $value2, big, BigEndian, from_be_bytes);
327            test_wrapped_field!(@case, $type, $expected_size, $value1, $value2, native, NativeEndian, from_ne_bytes);
328        };
329        (@case, $type:ty, $expected_size:expr, $value1:expr, $value2: expr, $endian:ident, $endian_type:ty, $endian_fn:ident) => {
330            $crate::internal::paste! {
331                #[test]
332                fn [<test_ $type _ $endian endian>]() {
333                    let mut storage = [0; 1024];
334
335                    type Field1 = WrappedField<$type, Wrapped<$type>, PrimitiveField<$type, $endian_type, 5>>;
336                    type Field2 = WrappedField<$type, Wrapped<$type>, PrimitiveField<$type, $endian_type, 123>>;
337
338                    Field1::write(&mut storage, Wrapped($value1));
339                    Field2::write(&mut storage, Wrapped($value2));
340
341                    assert_eq!(Wrapped($value1), Field1::read(&storage));
342                    assert_eq!(Wrapped($value2), Field2::read(&storage));
343
344                    assert_eq!($value1, <$type>::$endian_fn((&storage[5..(5+$expected_size)]).try_into().unwrap()));
345                    assert_eq!(
346                        $value2,
347                        <$type>::$endian_fn((&storage[123..(123+$expected_size)]).try_into().unwrap())
348                    );
349
350                    assert_eq!(Some($expected_size), Field1::SIZE);
351                    assert_eq!(5, Field1::OFFSET);
352                    assert_eq!(Some($expected_size), Field2::SIZE);
353                    assert_eq!(123, Field2::OFFSET);
354                }
355            }
356        };
357    }
358
359    test_wrapped_field!(i8, 1, 50, -20);
360    test_wrapped_field!(i16, 2, 500, -2000);
361    test_wrapped_field!(i32, 4, 10i32.pow(8), -(10i32.pow(7)));
362    test_wrapped_field!(i64, 8, 10i64.pow(15), -(10i64.pow(14)));
363    test_wrapped_field!(i128, 16, 10i128.pow(30), -(10i128.pow(28)));
364
365    test_wrapped_field!(u8, 1, 50, 20);
366    test_wrapped_field!(u16, 2, 500, 2000);
367    test_wrapped_field!(u32, 4, 10u32.pow(8), (10u32.pow(7)));
368    test_wrapped_field!(u64, 8, 10u64.pow(15), (10u64.pow(14)));
369    test_wrapped_field!(u128, 16, 10u128.pow(30), (10u128.pow(28)));
370
371    test_wrapped_field!(f32, 4, 10f32.powf(8.31), -(10f32.powf(7.31)));
372    test_wrapped_field!(f64, 8, 10f64.powf(15.31), -(10f64.powf(15.31)));
373
374    macro_rules! test_wrapped_unit_field {
375        ($endian:ident, $endian_type:ty) => {
376            $crate::internal::paste! {
377                #[allow(clippy::unit_cmp)]
378                #[test]
379                fn [<test_unit_ $endian endian>]() {
380                    let mut storage = [0; 1024];
381
382                    type Field1 = WrappedField<(), Wrapped<()>, PrimitiveField<(), LittleEndian, 5>>;
383                    type Field2 = WrappedField<(), Wrapped<()>, PrimitiveField<(), LittleEndian, 123>>;
384
385                    Field1::write(&mut storage, Wrapped(()));
386                    Field2::write(&mut storage, Wrapped(()));
387
388                    assert_eq!(Wrapped(()), Field1::read(&storage));
389                    assert_eq!(Wrapped(()), Field2::read(&storage));
390
391                    assert_eq!(Some(0), Field1::SIZE);
392                    assert_eq!(5, Field1::OFFSET);
393                    assert_eq!(Some(0), Field2::SIZE);
394                    assert_eq!(123, Field2::OFFSET);
395
396                    // Zero-sized types do not mutate the storage, so it should remain
397                    // unchanged for all of time.
398                    assert_eq!(storage, [0; 1024]);
399                }
400            }
401        };
402    }
403
404    test_wrapped_unit_field!(little, LittleEndian);
405    test_wrapped_unit_field!(big, BigEndian);
406    test_wrapped_unit_field!(native, NativeEndian);
407
408    mod read_error_write_infallible {
409        use super::*;
410        use crate::WrappedFieldError;
411
412        #[derive(Debug)]
413        struct ErrorType;
414
415        const SUCCESS_VALUE: u8 = 10;
416        const ERROR_VALUE: u8 = 100;
417
418        #[derive(Debug, PartialEq, Eq)]
419        struct Wrapped(u8);
420        impl LayoutAs<u8> for Wrapped {
421            type ReadError = ErrorType;
422            type WriteError = Infallible;
423
424            fn try_read(v: u8) -> Result<Self, ErrorType> {
425                if v == ERROR_VALUE {
426                    Err(ErrorType)
427                } else {
428                    Ok(Self(v))
429                }
430            }
431            fn try_write(v: Self) -> Result<u8, Infallible> {
432                Ok(v.0)
433            }
434        }
435
436        #[test]
437        fn test_metadata() {
438            type Field1 = WrappedField<u8, Wrapped, PrimitiveField<u8, LittleEndian, 5>>;
439            assert_eq!(Some(1), Field1::SIZE);
440            assert_eq!(5, Field1::OFFSET);
441        }
442
443        #[test]
444        fn test_success() {
445            let mut storage = [0; 1024];
446            type Field1 = WrappedField<u8, Wrapped, PrimitiveField<u8, LittleEndian, 5>>;
447
448            Field1::write(&mut storage, Wrapped(SUCCESS_VALUE));
449            assert_eq!(Wrapped(SUCCESS_VALUE), Field1::try_read(&storage).unwrap());
450
451            assert_eq!(
452                SUCCESS_VALUE,
453                u8::from_le_bytes((&storage[5..6]).try_into().unwrap())
454            );
455        }
456
457        #[test]
458        fn test_read_error() {
459            let mut storage = [0; 1024];
460            type Field1 = WrappedField<u8, Wrapped, PrimitiveField<u8, LittleEndian, 5>>;
461
462            storage[5..6].copy_from_slice(&ERROR_VALUE.to_le_bytes());
463            assert!(matches!(
464                Field1::try_read(&storage),
465                Err(WrappedFieldError::LayoutAsError(ErrorType)),
466            ));
467
468            assert_eq!(
469                ERROR_VALUE,
470                u8::from_le_bytes((&storage[5..6]).try_into().unwrap())
471            );
472        }
473    }
474
475    mod read_infallible_write_error {
476        use super::*;
477        use crate::WrappedFieldError;
478
479        #[derive(Debug)]
480        struct ErrorType;
481        const ERROR_VALUE: u8 = 100;
482        const SUCCESS_VALUE: u8 = 10;
483
484        #[derive(Debug, PartialEq, Eq)]
485        struct Wrapped(u8);
486        impl LayoutAs<u8> for Wrapped {
487            type ReadError = Infallible;
488            type WriteError = ErrorType;
489
490            fn try_read(v: u8) -> Result<Self, Infallible> {
491                Ok(Self(v))
492            }
493            fn try_write(v: Self) -> Result<u8, ErrorType> {
494                if v.0 == ERROR_VALUE {
495                    Err(ErrorType)
496                } else {
497                    Ok(v.0)
498                }
499            }
500        }
501
502        #[test]
503        fn test_metadata() {
504            type Field1 = WrappedField<u8, Wrapped, PrimitiveField<u8, LittleEndian, 5>>;
505            assert_eq!(Some(1), Field1::SIZE);
506            assert_eq!(5, Field1::OFFSET);
507        }
508
509        #[test]
510        fn test_success() {
511            let mut storage = [0; 1024];
512            type Field1 = WrappedField<u8, Wrapped, PrimitiveField<u8, LittleEndian, 5>>;
513
514            Field1::try_write(&mut storage, Wrapped(SUCCESS_VALUE)).unwrap();
515            assert_eq!(Wrapped(SUCCESS_VALUE), Field1::read(&storage));
516
517            assert_eq!(
518                SUCCESS_VALUE,
519                u8::from_le_bytes((&storage[5..6]).try_into().unwrap())
520            );
521        }
522
523        #[test]
524        fn test_write_error() {
525            let mut storage = [0; 1024];
526            type Field1 = WrappedField<u8, Wrapped, PrimitiveField<u8, LittleEndian, 5>>;
527
528            assert!(matches!(
529                Field1::try_write(&mut storage, Wrapped(ERROR_VALUE)),
530                Err(WrappedFieldError::LayoutAsError(ErrorType)),
531            ));
532
533            assert_eq!(0, u8::from_le_bytes((&storage[5..6]).try_into().unwrap()));
534        }
535    }
536
537    mod read_error_write_error {
538        use super::*;
539        use crate::WrappedFieldError;
540
541        #[derive(Debug)]
542        struct ReadErrorType;
543        #[derive(Debug)]
544        struct WriteErrorType;
545        const ERROR_VALUE: u8 = 100;
546        const SUCCESS_VALUE: u8 = 10;
547
548        #[derive(Debug, PartialEq, Eq)]
549        struct Wrapped(u8);
550        impl LayoutAs<u8> for Wrapped {
551            type ReadError = ReadErrorType;
552            type WriteError = WriteErrorType;
553
554            fn try_read(v: u8) -> Result<Self, ReadErrorType> {
555                if v == ERROR_VALUE {
556                    Err(ReadErrorType)
557                } else {
558                    Ok(Wrapped(v))
559                }
560            }
561            fn try_write(v: Self) -> Result<u8, WriteErrorType> {
562                if v.0 == ERROR_VALUE {
563                    Err(WriteErrorType)
564                } else {
565                    Ok(v.0)
566                }
567            }
568        }
569
570        #[test]
571        fn test_metadata() {
572            type Field1 = WrappedField<u8, Wrapped, PrimitiveField<u8, LittleEndian, 5>>;
573            assert_eq!(Some(1), Field1::SIZE);
574            assert_eq!(5, Field1::OFFSET);
575        }
576
577        #[test]
578        fn test_success() {
579            let mut storage = [0; 1024];
580            type Field1 = WrappedField<u8, Wrapped, PrimitiveField<u8, LittleEndian, 5>>;
581
582            Field1::try_write(&mut storage, Wrapped(SUCCESS_VALUE)).unwrap();
583            assert_eq!(Wrapped(SUCCESS_VALUE), Field1::try_read(&storage).unwrap());
584
585            assert_eq!(
586                SUCCESS_VALUE,
587                u8::from_le_bytes((&storage[5..6]).try_into().unwrap())
588            );
589        }
590
591        #[test]
592        fn test_read_error() {
593            let mut storage = [0; 1024];
594            type Field1 = WrappedField<u8, Wrapped, PrimitiveField<u8, LittleEndian, 5>>;
595
596            storage[5..6].copy_from_slice(&ERROR_VALUE.to_le_bytes());
597            assert!(matches!(
598                Field1::try_read(&storage),
599                Err(WrappedFieldError::LayoutAsError(ReadErrorType)),
600            ));
601
602            assert_eq!(
603                ERROR_VALUE,
604                u8::from_le_bytes((&storage[5..6]).try_into().unwrap())
605            );
606        }
607
608        #[test]
609        fn test_write_error() {
610            let mut storage = [0; 1024];
611            type Field1 = WrappedField<u8, Wrapped, PrimitiveField<u8, LittleEndian, 5>>;
612
613            assert!(matches!(
614                Field1::try_write(&mut storage, Wrapped(ERROR_VALUE)),
615                Err(WrappedFieldError::LayoutAsError(WriteErrorType)),
616            ));
617
618            assert_eq!(0, u8::from_le_bytes((&storage[5..6]).try_into().unwrap()));
619        }
620    }
621}