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}