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! nonzero_int_field {
9 ($type:ty, $zero_type:ty) => {
10 impl<E: Endianness, const OFFSET_: usize> FieldCopyAccess for PrimitiveField<$type, E, OFFSET_> {
11 type ReadError = NonZeroIsZeroError;
13 type WriteError = Infallible;
15 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]) -> Result<",stringify!($type), ", NonZeroIsZeroError>{
34 let read: ", stringify!($type), " = my_layout::some_integer_field::try_read(storage_data)?;
35 Ok(read)
36 }
37 ```
38 "},
39 #[inline(always)]
40 fn try_read(storage: &[u8]) -> Result<$type, NonZeroIsZeroError> {
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 => <$zero_type>::from_be_bytes(value),
44 EndianKind::Little => <$zero_type>::from_le_bytes(value),
45 EndianKind::Native => <$zero_type>::from_ne_bytes(value)
46 };
47 <$type>::new(value).ok_or(NonZeroIsZeroError(()))
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 let value = ", stringify!($type), "::new(10).unwrap();
69 my_layout::some_integer_field::try_write(storage_data, value).unwrap();
70 }
71 ```
72 "},
73 #[inline(always)]
74 fn try_write(storage: &mut [u8], value: $type) -> Result<(), Infallible> {
75 let value_as_bytes = match E::KIND {
76 EndianKind::Big => value.get().to_be_bytes(),
77 EndianKind::Little => value.get().to_le_bytes(),
78 EndianKind::Native => value.get().to_ne_bytes(),
79 };
80 storage[Self::OFFSET..(Self::OFFSET + core::mem::size_of::<$type>())]
81 .copy_from_slice(&value_as_bytes);
82 Ok(())
83 }
84 }
85 }
86
87 impl_field_traits!($type);
88 };
89}
90
91#[derive(Debug)]
94pub struct NonZeroIsZeroError(pub(crate) ());
95
96impl core::fmt::Display for NonZeroIsZeroError {
97 fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
98 write!(fmt, "NonZeroIsZeroError")
99 }
100}
101
102#[cfg(feature = "std")]
103impl std::error::Error for NonZeroIsZeroError {}
104
105nonzero_int_field!(core::num::NonZeroI8, i8);
106nonzero_int_field!(core::num::NonZeroI16, i16);
107nonzero_int_field!(core::num::NonZeroI32, i32);
108nonzero_int_field!(core::num::NonZeroI64, i64);
109nonzero_int_field!(core::num::NonZeroI128, i128);
110nonzero_int_field!(core::num::NonZeroU8, u8);
111nonzero_int_field!(core::num::NonZeroU16, u16);
112nonzero_int_field!(core::num::NonZeroU32, u32);
113nonzero_int_field!(core::num::NonZeroU64, u64);
114nonzero_int_field!(core::num::NonZeroU128, u128);
115
116#[cfg(test)]
117mod tests {
118 use crate::prelude::*;
119 use crate::PrimitiveField;
120
121 macro_rules! test_nonzero {
122 ($type:ty, $underlying_type:ty, $expected_size:expr, $value1:expr, $value2:expr) => {
123 test_nonzero!(@case, $type, $underlying_type, $expected_size, $value1, $value2, little, LittleEndian, from_le_bytes);
124 test_nonzero!(@case, $type, $underlying_type, $expected_size, $value1, $value2, big, BigEndian, from_be_bytes);
125 test_nonzero!(@case, $type, $underlying_type, $expected_size, $value1, $value2, native, NativeEndian, from_ne_bytes);
126 };
127 (@case, $type:ty, $underlying_type:ty, $expected_size:expr, $value1:expr, $value2: expr, $endian:ident, $endian_type:ty, $endian_fn:ident) => {
128 $crate::internal::paste! {
129 #[allow(non_snake_case)]
130 #[test]
131 fn [<test_ $type _ $endian endian_metadata>]() {
132 type Field1 = PrimitiveField<$type, $endian_type, 5>;
133 type Field2 = PrimitiveField<$type, $endian_type, 123>;
134 type Field3 = PrimitiveField<$type, $endian_type, 150>;
135
136 assert_eq!(Some($expected_size), Field1::SIZE);
137 assert_eq!(5, Field1::OFFSET);
138 assert_eq!(Some($expected_size), Field2::SIZE);
139 assert_eq!(123, Field2::OFFSET);
140 assert_eq!(Some($expected_size), Field3::SIZE);
141 assert_eq!(150, Field3::OFFSET);
142 }
143
144 #[allow(non_snake_case)]
145 #[test]
146 fn [<test_ $type _ $endian endian_fieldapi_tryread_write>]() {
147 let mut storage = [0; 1024];
148
149 let value1 = <$type>::new($value1).unwrap();
150 let value2 = <$type>::new($value2).unwrap();
151
152 type Field1 = PrimitiveField<$type, $endian_type, 5>;
153 type Field2 = PrimitiveField<$type, $endian_type, 123>;
154 type Field3 = PrimitiveField<$type, $endian_type, 150>;
155
156 Field1::write(&mut storage, value1);
157 Field2::write(&mut storage, value2);
158 assert_eq!(value1, Field1::try_read(&storage).unwrap());
161 assert_eq!(value2, Field2::try_read(&storage).unwrap());
162 assert!(matches!(Field3::try_read(&storage), Err(NonZeroIsZeroError(_))));
163
164 assert_eq!(value1, $type::new($underlying_type::$endian_fn((&storage[5..(5+$expected_size)]).try_into().unwrap())).unwrap());
165 assert_eq!(value2, $type::new($underlying_type::$endian_fn((&storage[123..(123+$expected_size)]).try_into().unwrap())).unwrap());
166 assert_eq!(0, $underlying_type::$endian_fn((&storage[150..(150+$expected_size)]).try_into().unwrap()));
167 }
168
169 #[allow(non_snake_case)]
170 #[test]
171 fn [<test_ $type _ $endian endian_fieldapi_tryread_trywrite>]() {
172 use crate::InfallibleResultExt;
173
174 let mut storage = [0; 1024];
175
176 let value1 = <$type>::new($value1).unwrap();
177 let value2 = <$type>::new($value2).unwrap();
178
179 type Field1 = PrimitiveField<$type, $endian_type, 5>;
180 type Field2 = PrimitiveField<$type, $endian_type, 123>;
181 type Field3 = PrimitiveField<$type, $endian_type, 150>;
182
183 Field1::try_write(&mut storage, value1).infallible_unwrap();
184 Field2::try_write(&mut storage, value2).infallible_unwrap();
185 assert_eq!(value1, Field1::try_read(&storage).unwrap());
188 assert_eq!(value2, Field2::try_read(&storage).unwrap());
189 assert!(matches!(Field3::try_read(&storage), Err(NonZeroIsZeroError(_))));
190
191 assert_eq!(value1, $type::new($underlying_type::$endian_fn((&storage[5..(5+$expected_size)]).try_into().unwrap())).unwrap());
192 assert_eq!(value2, $type::new($underlying_type::$endian_fn((&storage[123..(123+$expected_size)]).try_into().unwrap())).unwrap());
193 assert_eq!(0, $underlying_type::$endian_fn((&storage[150..(150+$expected_size)]).try_into().unwrap()));
194 }
195
196 #[allow(non_snake_case)]
197 #[test]
198 fn [<test_ $type _ $endian endian_viewapi_tryread_write>]() {
199 binary_layout!(layout, $endian_type, {
200 field1: $type,
201 field2: $type,
202 field3: $type,
203 });
204 let mut storage = [0; 1024];
205 let mut view = layout::View::new(&mut storage);
206
207 let value1 = <$type>::new($value1).unwrap();
208 let value2 = <$type>::new($value2).unwrap();
209
210 view.field1_mut().write(value1);
211 view.field2_mut().write(value2);
212 assert_eq!(value1, view.field1().try_read().unwrap());
215 assert_eq!(value2, view.field2().try_read().unwrap());
216 assert!(matches!(view.field3().try_read(), Err(NonZeroIsZeroError(_))));
217
218 assert_eq!(value1, $type::new($underlying_type::$endian_fn((&storage[0..($expected_size)]).try_into().unwrap())).unwrap());
219 assert_eq!(value2, $type::new($underlying_type::$endian_fn((&storage[$expected_size..(2*$expected_size)]).try_into().unwrap())).unwrap());
220 assert_eq!(0, $underlying_type::$endian_fn((&storage[2*$expected_size..(3*$expected_size)]).try_into().unwrap()));
221 }
222
223 #[allow(non_snake_case)]
224 #[test]
225 fn [<test_ $type _ $endian endian_viewapi_tryread_trywrite>]() {
226 binary_layout!(layout, $endian_type, {
227 field1: $type,
228 field2: $type,
229 field3: $type,
230 });
231 let mut storage = [0; 1024];
232 let mut view = layout::View::new(&mut storage);
233
234 let value1 = <$type>::new($value1).unwrap();
235 let value2 = <$type>::new($value2).unwrap();
236
237 view.field1_mut().try_write(value1).infallible_unwrap();
238 view.field2_mut().try_write(value2).infallible_unwrap();
239 assert_eq!(value1, view.field1().try_read().unwrap());
242 assert_eq!(value2, view.field2().try_read().unwrap());
243 assert!(matches!(view.field3().try_read(), Err(NonZeroIsZeroError(_))));
244
245 assert_eq!(value1, $type::new($underlying_type::$endian_fn((&storage[0..($expected_size)]).try_into().unwrap())).unwrap());
246 assert_eq!(value2, $type::new($underlying_type::$endian_fn((&storage[$expected_size..(2*$expected_size)]).try_into().unwrap())).unwrap());
247 assert_eq!(0, $underlying_type::$endian_fn((&storage[2*$expected_size..(3*$expected_size)]).try_into().unwrap()));
248 }
249 }
250 };
251 }
252
253 use core::num::{
254 NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroU128, NonZeroU16,
255 NonZeroU32, NonZeroU64, NonZeroU8,
256 };
257
258 test_nonzero!(NonZeroI8, i8, 1, 50, -20);
259 test_nonzero!(NonZeroI16, i16, 2, 500, -2000);
260 test_nonzero!(NonZeroI32, i32, 4, 10i32.pow(8), -(10i32.pow(7)));
261 test_nonzero!(NonZeroI64, i64, 8, 10i64.pow(15), -(10i64.pow(14)));
262 test_nonzero!(NonZeroI128, i128, 16, 10i128.pow(30), -(10i128.pow(28)));
263
264 test_nonzero!(NonZeroU8, u8, 1, 50, 20);
265 test_nonzero!(NonZeroU16, u16, 2, 500, 2000);
266 test_nonzero!(NonZeroU32, u32, 4, 10u32.pow(8), (10u32.pow(7)));
267 test_nonzero!(NonZeroU64, u64, 8, 10u64.pow(15), (10u64.pow(14)));
268 test_nonzero!(NonZeroU128, u128, 16, 10u128.pow(30), (10u128.pow(28)));
269}