binary_layout/fields/primitive/
view.rs

1use core::marker::PhantomData;
2
3use crate::{Field, FieldCopyAccess, FieldReadExt, FieldWriteExt};
4
5/// A field view represents the field metadata stored in a [Field] plus it stores the underlying
6/// storage data it operates on, either as a reference to a slice `&[u8]`, `&mut [u8]`, or as
7/// an owning [`Vec<u8>`].
8///
9/// Since this API remembers the underlying storage data in a view object, you don't have to pass it
10/// in each time you're accessing a field. If you rather prefer an API that does not do that,
11/// take a look at the [Field] API.
12///
13/// # Example:
14/// ```
15/// use binary_layout::prelude::*;
16///
17/// binary_layout!(my_layout, LittleEndian, {
18///   field_one: u16,
19///   another_field: [u8; 16],
20///   something_else: u32,
21///   tail: [u8],
22/// });
23///
24/// fn func(storage_data: &mut [u8]) {
25///   let mut view = my_layout::View::new(storage_data);
26///
27///   // read some data
28///   let format_version_header: u16 = view.field_one().read();
29///   // equivalent: let format_version_header = u16::from_le_bytes((&storage_data[0..2]).try_into().unwrap());
30///
31///   // write some data
32///   view.something_else_mut().write(10);
33///   // equivalent: data_slice[18..22].copy_from_slice(&10u32.to_le_bytes());
34///
35///   // access a data region
36///   let tail: &[u8] = view.tail();
37///   // equivalent: let tail: &[u8] = &data_slice[22..];
38///
39///   // and modify it
40///   view.tail_mut()[..5].copy_from_slice(&[1, 2, 3, 4, 5]);
41///   // equivalent: data_slice[18..22].copy_from_slice(&[1, 2, 3, 4, 5]);
42/// }
43/// ```
44pub struct FieldView<S, F: Field> {
45    storage: S,
46    _p: PhantomData<F>,
47}
48
49impl<S, F: Field> FieldView<S, F> {
50    /// Create a new view for a field over a given storage.
51    /// You probably shouldn't call this directly but should instead call
52    /// `your_layout::View::new()`, which is generated by the
53    /// [binary_layout!](crate::binary_layout!) macro for you.
54    #[inline(always)]
55    pub fn new(storage: S) -> Self {
56        Self {
57            storage,
58            _p: PhantomData,
59        }
60    }
61}
62impl<S: AsRef<[u8]>, F: FieldReadExt> FieldView<S, F> {
63    /// Read the field from a given data region, assuming the defined layout, using the [FieldView] API.
64    ///
65    /// # Example
66    /// ```
67    /// use binary_layout::prelude::*;
68    ///
69    /// binary_layout!(my_layout, LittleEndian, {
70    ///   //... other fields ...
71    ///   some_integer_field: i8
72    ///   //... other fields ...
73    /// });
74    ///
75    /// fn func(storage_data: &[u8]) {
76    ///   let view = my_layout::View::new(storage_data);
77    ///   let read: i8 = view.some_integer_field().read();
78    /// }
79    /// ```
80    #[inline(always)]
81    pub fn read(&self) -> F::HighLevelType {
82        F::read(self.storage.as_ref())
83    }
84}
85impl<S: AsMut<[u8]>, F: FieldWriteExt> FieldView<S, F> {
86    /// Write the field to a given data region, assuming the defined layout, using the [FieldView] API.
87    ///
88    /// # Example
89    /// ```
90    /// use binary_layout::prelude::*;
91    ///
92    /// binary_layout!(my_layout, LittleEndian, {
93    ///   //... other fields ...
94    ///   some_integer_field: i8
95    ///   //... other fields ...
96    /// });
97    ///
98    /// fn func(storage_data: &mut [u8]) {
99    ///   let mut view = my_layout::View::new(storage_data);
100    ///   view.some_integer_field_mut().write(10);
101    /// }
102    /// ```
103    #[inline(always)]
104    pub fn write(&mut self, v: F::HighLevelType) {
105        F::write(self.storage.as_mut(), v)
106    }
107}
108impl<S: AsRef<[u8]>, F: FieldCopyAccess> FieldView<S, F> {
109    /// Read the field from a given data region, assuming the defined layout, using the [FieldView] API.
110    ///
111    /// # Example
112    /// ```
113    /// use binary_layout::prelude::*;
114    /// use core::num::NonZeroI8;
115    ///
116    /// binary_layout!(my_layout, LittleEndian, {
117    ///   //... other fields ...
118    ///   some_integer_field: core::num::NonZeroI8,
119    ///   //... other fields ...
120    /// });
121    ///
122    /// fn func(storage_data: &[u8]) -> Result<NonZeroI8, NonZeroIsZeroError> {
123    ///   let view = my_layout::View::new(storage_data);
124    ///   let read: NonZeroI8 = view.some_integer_field().try_read()?;
125    ///   Ok(read)
126    /// }
127    /// ```
128    #[inline(always)]
129    pub fn try_read(&self) -> Result<F::HighLevelType, F::ReadError> {
130        F::try_read(self.storage.as_ref())
131    }
132}
133impl<S: AsMut<[u8]>, F: FieldCopyAccess> FieldView<S, F> {
134    /// Write the field to a given data region, assuming the defined layout, using the [FieldView] API.
135    ///
136    /// # Example
137    /// ```
138    /// use binary_layout::prelude::*;
139    /// use core::num::NonZeroI8;
140    /// use core::convert::Infallible;
141    ///
142    /// binary_layout!(my_layout, LittleEndian, {
143    ///   //... other fields ...
144    ///   some_integer_field: core::num::NonZeroI8,
145    ///   //... other fields ...
146    /// });
147    ///
148    /// fn func(storage_data: &mut [u8]) -> Result<(), Infallible> {
149    ///   let mut view = my_layout::View::new(storage_data);
150    ///   let value = NonZeroI8::new(10).unwrap();
151    ///   view.some_integer_field_mut().try_write(value)?;
152    ///   Ok(())
153    /// }
154    /// ```
155    #[inline(always)]
156    pub fn try_write(&mut self, v: F::HighLevelType) -> Result<(), F::WriteError> {
157        F::try_write(self.storage.as_mut(), v)
158    }
159}