binary_layout/fields/primitive/copy_access/
primitive_float.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! float_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 float 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                use core::convert::Infallible;
27
28                binary_layout!(my_layout, LittleEndian, {
29                    //... other fields ...
30                    some_float_field: ", stringify!($type), "
31                    //... other fields ...
32                });
33
34                fn func(storage_data: &[u8]) -> ", stringify!($type), " {
35                    let read: ", stringify!($type), " = my_layout::some_float_field::try_read(storage_data).unwrap();
36                    read
37                }
38                ```
39
40                # WARNING
41
42                At it's core, this method uses [", stringify!($type), "::from_bits](https://doc.rust-lang.org/std/primitive.", stringify!($type), ".html#method.from_bits),
43                which has some weird behavior around signaling and non-signaling `NaN` values.  Read the
44                documentation for [", stringify!($type), "::from_bits](https://doc.rust-lang.org/std/primitive.", stringify!($type), ".html#method.from_bits) which
45                explains the situation.
46                "},
47                #[inline(always)]
48                fn try_read(storage: &[u8]) -> Result<$type, Infallible> {
49                    let value: [u8; core::mem::size_of::<$type>()] = storage[Self::OFFSET..(Self::OFFSET + core::mem::size_of::<$type>())].try_into().unwrap();
50                    let value = match E::KIND {
51                        EndianKind::Big => <$type>::from_be_bytes(value),
52                        EndianKind::Little => <$type>::from_le_bytes(value),
53                        EndianKind::Native => <$type>::from_ne_bytes(value),
54                    };
55                    Ok(value)
56                }
57            }
58
59            doc_comment::doc_comment! {
60                concat! {"
61                Write the float field to a given data region, assuming the defined layout, using the [Field] API.
62
63                # Example:
64
65                ```
66                use binary_layout::prelude::*;
67
68                binary_layout!(my_layout, LittleEndian, {
69                    //... other fields ...
70                    some_float_field: ", stringify!($type), "
71                    //... other fields ...
72                });
73
74                fn func(storage_data: &mut [u8]) {
75                    my_layout::some_float_field::try_write(storage_data, 10.0).unwrap();
76                }
77                ```
78
79                # WARNING
80
81                At it's core, this method uses [", stringify!($type), "::to_bits](https://doc.rust-lang.org/std/primitive.", stringify!($type), ".html#method.to_bits),
82                which has some weird behavior around signaling and non-signaling `NaN` values.  Read the
83                documentation for [", stringify!($type), "::to_bits](https://doc.rust-lang.org/std/primitive.", stringify!($type), ".html#method.to_bits) which
84                explains the situation.
85                "},
86                #[inline(always)]
87                fn try_write(storage: &mut [u8], value: $type) -> Result<(), Infallible> {
88                    let value_as_bytes = match E::KIND {
89                        EndianKind::Big => value.to_be_bytes(),
90                        EndianKind::Little => value.to_le_bytes(),
91                        EndianKind::Native => value.to_ne_bytes(),
92                    };
93                    storage[Self::OFFSET..(Self::OFFSET + core::mem::size_of::<$type>())]
94                        .copy_from_slice(&value_as_bytes);
95                    Ok(())
96                }
97            }
98        }
99
100        impl_field_traits!($type);
101    };
102}
103
104float_field!(f32);
105float_field!(f64);
106
107#[cfg(test)]
108mod tests {
109    #![allow(clippy::float_cmp)]
110    use crate::prelude::*;
111    use crate::PrimitiveField;
112
113    macro_rules! test_float {
114        ($type:ty, $expected_size:expr, $value1:expr, $value2:expr) => {
115            test_float!(@case, $type, $expected_size, $value1, $value2, little, LittleEndian, from_le_bytes);
116            test_float!(@case, $type, $expected_size, $value1, $value2, big, BigEndian, from_be_bytes);
117            test_float!(@case, $type, $expected_size, $value1, $value2, native, NativeEndian, from_ne_bytes);
118        };
119        (@case, $type:ty, $expected_size:expr, $value1:expr, $value2: expr, $endian:ident, $endian_type:ty, $endian_fn:ident) => {
120            $crate::internal::paste! {
121                #[allow(non_snake_case)]
122                #[test]
123                fn [<test_ $type _ $endian endian_metadata>]() {
124                    type Field1 = PrimitiveField<$type, $endian_type, 5>;
125                    type Field2 = PrimitiveField<$type, $endian_type, 123>;
126                    type Field3 = PrimitiveField<$type, $endian_type, 150>;
127
128                    assert_eq!(Some($expected_size), Field1::SIZE);
129                    assert_eq!(5, Field1::OFFSET);
130                    assert_eq!(Some($expected_size), Field2::SIZE);
131                    assert_eq!(123, Field2::OFFSET);
132                    assert_eq!(Some($expected_size), Field3::SIZE);
133                    assert_eq!(150, Field3::OFFSET);
134                }
135
136                #[allow(non_snake_case)]
137                #[test]
138                fn [<test_ $type _ $endian endian_fieldapi_read_write>]() {
139                    let mut storage = [0; 1024];
140
141                    type Field1 = PrimitiveField<$type, $endian_type, 5>;
142                    type Field2 = PrimitiveField<$type, $endian_type, 123>;
143                    type Field3 = PrimitiveField<$type, $endian_type, 150>;
144
145                    Field1::write(&mut storage, $value1);
146                    Field2::write(&mut storage, $value2);
147                    Field3::write(&mut storage, 0.0);
148
149                    assert_eq!($value1, Field1::read(&storage));
150                    assert_eq!($value2, Field2::read(&storage));
151                    assert_eq!(0.0, Field3::read(&storage));
152
153                    assert_eq!($value1, $type::$endian_fn((&storage[5..(5+$expected_size)]).try_into().unwrap()));
154                    assert_eq!($value2, $type::$endian_fn((&storage[123..(123+$expected_size)]).try_into().unwrap()));
155                    assert_eq!(0.0, $type::$endian_fn((&storage[150..(150+$expected_size)]).try_into().unwrap()));
156                }
157
158                #[allow(non_snake_case)]
159                #[test]
160                fn [<test_ $type _ $endian endian_fieldapi_tryread_trywrite>]() {
161                    use crate::InfallibleResultExt;
162
163                    let mut storage = [0; 1024];
164
165                    type Field1 = PrimitiveField<$type, $endian_type, 5>;
166                    type Field2 = PrimitiveField<$type, $endian_type, 123>;
167                    type Field3 = PrimitiveField<$type, $endian_type, 150>;
168
169                    Field1::try_write(&mut storage, $value1).infallible_unwrap();
170                    Field2::try_write(&mut storage, $value2).infallible_unwrap();
171                    Field3::try_write(&mut storage, 0.0).infallible_unwrap();
172
173                    assert_eq!($value1, Field1::try_read(&storage).infallible_unwrap());
174                    assert_eq!($value2, Field2::try_read(&storage).infallible_unwrap());
175                    assert_eq!(0.0, Field3::try_read(&storage).infallible_unwrap());
176
177                    assert_eq!($value1, $type::$endian_fn((&storage[5..(5+$expected_size)]).try_into().unwrap()));
178                    assert_eq!($value2, $type::$endian_fn((&storage[123..(123+$expected_size)]).try_into().unwrap()));
179                    assert_eq!(0.0, $type::$endian_fn((&storage[150..(150+$expected_size)]).try_into().unwrap()));
180                }
181
182                #[allow(non_snake_case)]
183                #[test]
184                fn [<test_ $type _ $endian endian_viewapi_read_write>]() {
185                    binary_layout!(layout, $endian_type, {
186                        field1: $type,
187                        field2: $type,
188                        field3: $type,
189                    });
190                    let mut storage = [0; 1024];
191                    let mut view = layout::View::new(&mut storage);
192
193                    view.field1_mut().write($value1);
194                    view.field2_mut().write($value2);
195                    view.field3_mut().write(0.0);
196
197                    assert_eq!($value1, view.field1().read());
198                    assert_eq!($value2, view.field2().read());
199                    assert_eq!(0.0, view.field3().read());
200
201                    assert_eq!($value1, $type::$endian_fn((&storage[0..(0+$expected_size)]).try_into().unwrap()));
202                    assert_eq!($value2, $type::$endian_fn((&storage[$expected_size..(2*$expected_size)]).try_into().unwrap()));
203                    assert_eq!(0.0, $type::$endian_fn((&storage[2*$expected_size..(3*$expected_size)]).try_into().unwrap()));
204                }
205
206                #[allow(non_snake_case)]
207                #[test]
208                fn [<test_ $type _ $endian endian_viewapi_tryread_trywrite>]() {
209                    binary_layout!(layout, $endian_type, {
210                        field1: $type,
211                        field2: $type,
212                        field3: $type,
213                    });
214                    let mut storage = [0; 1024];
215                    let mut view = layout::View::new(&mut storage);
216
217                    view.field1_mut().try_write($value1).infallible_unwrap();
218                    view.field2_mut().try_write($value2).infallible_unwrap();
219                    view.field3_mut().try_write(0.0).infallible_unwrap();
220
221                    assert_eq!($value1, view.field1().try_read().infallible_unwrap());
222                    assert_eq!($value2, view.field2().try_read().infallible_unwrap());
223                    assert_eq!(0.0, view.field3().try_read().infallible_unwrap());
224
225                    assert_eq!($value1, $type::$endian_fn((&storage[0..(0+$expected_size)]).try_into().unwrap()));
226                    assert_eq!($value2, $type::$endian_fn((&storage[$expected_size..(2*$expected_size)]).try_into().unwrap()));
227                    assert_eq!(0.0, $type::$endian_fn((&storage[2*$expected_size..(3*$expected_size)]).try_into().unwrap()));
228                }
229            }
230        };
231    }
232
233    test_float!(f32, 4, 10f32.powf(8.31), -(10f32.powf(7.31)));
234    test_float!(f64, 8, 10f64.powf(15.31), -(10f64.powf(15.31)));
235}