binary_layout/fields/primitive/copy_access/
primitive_int.rs

1use core::convert::Infallible;
2
3use super::{FieldCopyAccess, PrimitiveField};
4use crate::endianness::{EndianKind, Endianness};
5use crate::fields::primitive::view::FieldView;
6use crate::fields::{Field, StorageIntoFieldView, StorageToFieldView};
7
8macro_rules! int_field {
9    ($type:ty) => {
10        impl<E: Endianness, const OFFSET_: usize> FieldCopyAccess for PrimitiveField<$type, E, OFFSET_> {
11            /// See [FieldCopyAccess::ReadError]
12            type ReadError = Infallible;
13            /// See [FieldCopyAccess::WriteError]
14            type WriteError = Infallible;
15            /// See [FieldCopyAccess::HighLevelType]
16            type HighLevelType = $type;
17
18            doc_comment::doc_comment! {
19                concat! {"
20                Read the integer field from a given data region, assuming the defined layout, using the [Field] API.
21
22                # Example:
23
24                ```
25                use binary_layout::prelude::*;
26
27                binary_layout!(my_layout, LittleEndian, {
28                    //... other fields ...
29                    some_integer_field: ", stringify!($type), "
30                    //... other fields ...
31                });
32
33                fn func(storage_data: &[u8]) -> ",stringify!($type), " {
34                    let read: ", stringify!($type), " = my_layout::some_integer_field::try_read(storage_data).unwrap();
35                    read
36                }
37                ```
38                "},
39                #[inline(always)]
40                fn try_read(storage: &[u8]) -> Result<$type, Infallible> {
41                    let value: [u8; core::mem::size_of::<$type>()] = storage[Self::OFFSET..(Self::OFFSET + core::mem::size_of::<$type>())].try_into().unwrap();
42                    let value = match E::KIND {
43                        EndianKind::Big => <$type>::from_be_bytes(value),
44                        EndianKind::Little => <$type>::from_le_bytes(value),
45                        EndianKind::Native => <$type>::from_ne_bytes(value)
46                    };
47                    Ok(value)
48                }
49            }
50
51            doc_comment::doc_comment! {
52                concat! {"
53                Write the integer field to a given data region, assuming the defined layout, using the [Field] API.
54
55                # Example:
56
57                ```
58                use binary_layout::prelude::*;
59                use core::convert::Infallible;
60
61                binary_layout!(my_layout, LittleEndian, {
62                    //... other fields ...
63                    some_integer_field: ", stringify!($type), "
64                    //... other fields ...
65                });
66
67                fn func(storage_data: &mut [u8]) {
68                    my_layout::some_integer_field::try_write(storage_data, 10).unwrap();
69                }
70                ```
71                "},
72                #[inline(always)]
73                fn try_write(storage: &mut [u8], value: $type) -> Result<(), Infallible> {
74                    let value_as_bytes = match E::KIND {
75                        EndianKind::Big => value.to_be_bytes(),
76                        EndianKind::Little => value.to_le_bytes(),
77                        EndianKind::Native => value.to_ne_bytes(),
78                    };
79                    storage[Self::OFFSET..(Self::OFFSET + core::mem::size_of::<$type>())]
80                        .copy_from_slice(&value_as_bytes);
81                    Ok(())
82                }
83            }
84        }
85
86        impl_field_traits!($type);
87    };
88}
89
90int_field!(i8);
91int_field!(i16);
92int_field!(i32);
93int_field!(i64);
94int_field!(i128);
95int_field!(u8);
96int_field!(u16);
97int_field!(u32);
98int_field!(u64);
99int_field!(u128);
100
101#[cfg(test)]
102mod tests {
103    use crate::prelude::*;
104    use crate::PrimitiveField;
105
106    macro_rules! test_int {
107        ($type:ty, $expected_size:expr, $value1:expr, $value2:expr) => {
108            test_int!(@case, $type, $expected_size, $value1, $value2, little, LittleEndian, from_le_bytes);
109            test_int!(@case, $type, $expected_size, $value1, $value2, big, BigEndian, from_be_bytes);
110            test_int!(@case, $type, $expected_size, $value1, $value2, native, NativeEndian, from_ne_bytes);
111        };
112        (@case, $type:ty, $expected_size:expr, $value1:expr, $value2: expr, $endian:ident, $endian_type:ty, $endian_fn:ident) => {
113            $crate::internal::paste! {
114                #[allow(non_snake_case)]
115                #[test]
116                fn [<test_ $type _ $endian endian_metadata>]() {
117                    type Field1 = PrimitiveField<$type, $endian_type, 5>;
118                    type Field2 = PrimitiveField<$type, $endian_type, 123>;
119                    type Field3 = PrimitiveField<$type, $endian_type, 150>;
120
121                    assert_eq!(Some($expected_size), Field1::SIZE);
122                    assert_eq!(5, Field1::OFFSET);
123                    assert_eq!(Some($expected_size), Field2::SIZE);
124                    assert_eq!(123, Field2::OFFSET);
125                    assert_eq!(Some($expected_size), Field3::SIZE);
126                    assert_eq!(150, Field3::OFFSET);
127                }
128
129                #[allow(non_snake_case)]
130                #[test]
131                fn [<test_ $type _ $endian endian_fieldapi_read_write>]() {
132                    let mut storage = [0; 1024];
133
134                    type Field1 = PrimitiveField<$type, $endian_type, 5>;
135                    type Field2 = PrimitiveField<$type, $endian_type, 123>;
136                    type Field3 = PrimitiveField<$type, $endian_type, 150>;
137
138                    Field1::write(&mut storage, $value1);
139                    Field2::write(&mut storage, $value2);
140                    Field3::write(&mut storage, 0);
141
142                    assert_eq!($value1, Field1::read(&storage));
143                    assert_eq!($value2, Field2::read(&storage));
144                    assert_eq!(0, Field3::read(&storage));
145
146                    assert_eq!($value1, $type::$endian_fn((&storage[5..(5+$expected_size)]).try_into().unwrap()));
147                    assert_eq!($value2, $type::$endian_fn((&storage[123..(123+$expected_size)]).try_into().unwrap()));
148                    assert_eq!(0, $type::$endian_fn((&storage[150..(150+$expected_size)]).try_into().unwrap()));
149                }
150
151                #[allow(non_snake_case)]
152                #[test]
153                fn [<test_ $type _ $endian endian_fieldapi_tryread_trywrite>]() {
154                    use crate::InfallibleResultExt;
155
156                    let mut storage = [0; 1024];
157
158                    type Field1 = PrimitiveField<$type, $endian_type, 5>;
159                    type Field2 = PrimitiveField<$type, $endian_type, 123>;
160                    type Field3 = PrimitiveField<$type, $endian_type, 150>;
161
162                    Field1::try_write(&mut storage, $value1).infallible_unwrap();
163                    Field2::try_write(&mut storage, $value2).infallible_unwrap();
164                    Field3::try_write(&mut storage, 0).infallible_unwrap();
165
166                    assert_eq!($value1, Field1::try_read(&storage).infallible_unwrap());
167                    assert_eq!($value2, Field2::try_read(&storage).infallible_unwrap());
168                    assert_eq!(0, Field3::try_read(&storage).infallible_unwrap());
169
170                    assert_eq!($value1, $type::$endian_fn((&storage[5..(5+$expected_size)]).try_into().unwrap()));
171                    assert_eq!($value2, $type::$endian_fn((&storage[123..(123+$expected_size)]).try_into().unwrap()));
172                    assert_eq!(0, $type::$endian_fn((&storage[150..(150+$expected_size)]).try_into().unwrap()));
173                }
174
175                #[allow(non_snake_case)]
176                #[test]
177                fn [<test_ $type _ $endian endian_viewapi_read_write>]() {
178                    binary_layout!(layout, $endian_type, {
179                        field1: $type,
180                        field2: $type,
181                        field3: $type,
182                    });
183                    let mut storage = [0; 1024];
184                    let mut view = layout::View::new(&mut storage);
185
186                    view.field1_mut().write($value1);
187                    view.field2_mut().write($value2);
188                    view.field3_mut().write(0);
189
190                    assert_eq!($value1, view.field1().read());
191                    assert_eq!($value2, view.field2().read());
192                    assert_eq!(0, view.field3().read());
193
194                    assert_eq!($value1, $type::$endian_fn((&storage[0..(0+$expected_size)]).try_into().unwrap()));
195                    assert_eq!($value2, $type::$endian_fn((&storage[$expected_size..(2*$expected_size)]).try_into().unwrap()));
196                    assert_eq!(0, $type::$endian_fn((&storage[2*$expected_size..(3*$expected_size)]).try_into().unwrap()));
197                }
198
199                #[allow(non_snake_case)]
200                #[test]
201                fn [<test_ $type _ $endian endian_viewapi_tryread_trywrite>]() {
202                    binary_layout!(layout, $endian_type, {
203                        field1: $type,
204                        field2: $type,
205                        field3: $type,
206                    });
207                    let mut storage = [0; 1024];
208                    let mut view = layout::View::new(&mut storage);
209
210                    view.field1_mut().try_write($value1).infallible_unwrap();
211                    view.field2_mut().try_write($value2).infallible_unwrap();
212                    view.field3_mut().try_write(0).infallible_unwrap();
213
214                    assert_eq!($value1, view.field1().try_read().infallible_unwrap());
215                    assert_eq!($value2, view.field2().try_read().infallible_unwrap());
216                    assert_eq!(0, view.field3().try_read().infallible_unwrap());
217
218                    assert_eq!($value1, $type::$endian_fn((&storage[0..(0+$expected_size)]).try_into().unwrap()));
219                    assert_eq!($value2, $type::$endian_fn((&storage[$expected_size..(2*$expected_size)]).try_into().unwrap()));
220                    assert_eq!(0, $type::$endian_fn((&storage[2*$expected_size..(3*$expected_size)]).try_into().unwrap()));
221                }
222            }
223        };
224    }
225
226    test_int!(i8, 1, 50, -20);
227    test_int!(i16, 2, 500, -2000);
228    test_int!(i32, 4, 10i32.pow(8), -(10i32.pow(7)));
229    test_int!(i64, 8, 10i64.pow(15), -(10i64.pow(14)));
230    test_int!(i128, 16, 10i128.pow(30), -(10i128.pow(28)));
231
232    test_int!(u8, 1, 50, 20);
233    test_int!(u16, 2, 500, 2000);
234    test_int!(u32, 4, 10u32.pow(8), (10u32.pow(7)));
235    test_int!(u64, 8, 10u64.pow(15), (10u64.pow(14)));
236    test_int!(u128, 16, 10u128.pow(30), (10u128.pow(28)));
237}