1use core::ops::Deref;
5
6macro_rules! unaligned_integer_type {
16 ($name:ident, $bitsize:expr, $storage_type:ty, $min:expr, $max:expr, [$($additional_conversions:ty),*]) => {
17 #[allow(non_camel_case_types)]
18 #[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Default)]
19 pub struct $name($storage_type);
20
21 impl $name {
22 pub const ZERO: Self = Self(0);
23 pub const MIN: Self = Self($min);
24 pub const MAX: Self = Self($max);
25
26 #[inline]
28 pub fn new_truncated(value: $storage_type) -> Self {
29 Self(value & ((1 << $bitsize) - 1))
30 }
31
32 #[inline]
33 pub fn from_be_bytes(bytes: [u8; ($bitsize / 8)]) -> Self {
34 Self(UnalignedBytes::be_bytes_to_storage(bytes) as _)
35 }
36
37 #[inline]
38 pub fn to_be_bytes(self) -> [u8; ($bitsize / 8)] {
39 UnalignedBytes::storage_to_be_bytes(self.0 as _)
40 }
41 }
42
43 #[cfg(any(test, feature = "generator"))]
44 impl bolero_generator::TypeGenerator for $name {
45 fn generate<D: bolero_generator::Driver>(driver: &mut D) -> Option<Self> {
46 Some(Self::new_truncated(driver.produce()?))
47 }
48 }
49
50 impl TryFrom<$storage_type> for $name {
51 type Error = TryFromIntError;
52
53 #[inline]
54 fn try_from(value: $storage_type) -> Result<Self, Self::Error> {
55 if value < (1 << $bitsize) {
56 Ok(Self(value))
57 } else {
58 Err(TryFromIntError(()))
59 }
60 }
61 }
62
63 impl From<$name> for $storage_type {
64 #[inline]
65 fn from(value: $name) -> $storage_type {
66 value.0
67 }
68 }
69
70 $(
71 impl From<$additional_conversions> for $name {
72 #[inline]
73 fn from(value: $additional_conversions) -> Self {
74 $name(value.into())
75 }
76 }
77
78 impl From<$name> for $additional_conversions {
79 #[inline]
80 fn from(value: $name) -> Self {
81 value.0 as $additional_conversions
82 }
83 }
84 )*
85
86 impl Deref for $name {
87 type Target = $storage_type;
88
89 #[inline]
90 fn deref(&self) -> &Self::Target {
91 &self.0
92 }
93 }
94 };
95}
96
97trait UnalignedBytes: Sized {
99 type Storage;
100
101 fn storage_to_be_bytes(storage: Self::Storage) -> Self;
102 fn be_bytes_to_storage(self) -> Self::Storage;
103}
104
105impl UnalignedBytes for [u8; 3] {
106 type Storage = u32;
107
108 #[inline]
109 fn storage_to_be_bytes(storage: Self::Storage) -> Self {
110 let [_, a, b, c] = storage.to_be_bytes();
111 [a, b, c]
112 }
113
114 #[inline]
115 fn be_bytes_to_storage(self) -> Self::Storage {
116 let [a, b, c] = self;
117 let bytes = [0, a, b, c];
118 Self::Storage::from_be_bytes(bytes)
119 }
120}
121
122impl UnalignedBytes for [u8; 6] {
123 type Storage = u64;
124
125 #[inline]
126 fn storage_to_be_bytes(storage: Self::Storage) -> Self {
127 let [_, _, a, b, c, d, e, f] = storage.to_be_bytes();
128 [a, b, c, d, e, f]
129 }
130
131 #[inline]
132 fn be_bytes_to_storage(self) -> Self::Storage {
133 let [a, b, c, d, e, f] = self;
134 let bytes = [0, 0, a, b, c, d, e, f];
135 Self::Storage::from_be_bytes(bytes)
136 }
137}
138
139macro_rules! signed_min {
140 ($bitsize:expr) => {
141 -(1 << ($bitsize - 1))
142 };
143}
144
145macro_rules! signed_max {
146 ($bitsize:expr) => {
147 ((1 << ($bitsize - 1)) - 1)
148 };
149}
150
151#[test]
152fn signed_min_max_test() {
153 assert_eq!(i8::MIN as i16, signed_min!(8));
154 assert_eq!(i8::MAX as i16, signed_max!(8));
155}
156
157unaligned_integer_type!(u24, 24, u32, 0, (1 << 24) - 1, [u8, u16]);
158unaligned_integer_type!(
159 i24,
160 24,
161 i32,
162 signed_min!(24),
163 signed_max!(24),
164 [u8, i8, u16, i16]
165);
166
167impl TryFrom<u64> for u24 {
168 type Error = TryFromIntError;
169
170 #[inline]
171 fn try_from(value: u64) -> Result<Self, Self::Error> {
172 let storage_value: u32 = value.try_into()?;
173 storage_value.try_into()
174 }
175}
176
177impl From<u24> for u64 {
178 #[inline]
179 fn from(value: u24) -> u64 {
180 value.0.into()
181 }
182}
183
184unaligned_integer_type!(u48, 48, u64, 0, (1 << 48) - 1, [u8, u16, u32]);
185unaligned_integer_type!(
186 i48,
187 48,
188 i64,
189 signed_min!(48),
190 signed_max!(48),
191 [u8, i8, u16, i16, u32, i32]
192);
193
194#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Eq, Ord, Hash)]
195#[cfg_attr(test, derive(bolero::TypeGenerator))]
196pub struct TryFromIntError(());
197
198impl From<core::num::TryFromIntError> for TryFromIntError {
199 #[inline]
200 fn from(_: core::num::TryFromIntError) -> Self {
201 Self(())
202 }
203}
204
205impl core::fmt::Display for TryFromIntError {
206 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
207 write!(f, "TryFromIntError")
208 }
209}
210
211#[cfg(test)]
212mod tests {
213
214 use super::*;
215
216 #[test]
217 #[cfg_attr(kani, kani::proof)]
218 fn u8_len_3_be_bytes_to_storage() {
219 bolero::check!()
220 .with_type()
221 .for_each(|callee: &[u8; 3]| Some(callee.be_bytes_to_storage()));
222 }
223
224 #[test]
225 #[cfg_attr(kani, kani::proof)]
226 fn u8_len_6_be_bytes_to_storage() {
227 bolero::check!()
228 .with_type()
229 .for_each(|callee: &[u8; 6]| Some(callee.be_bytes_to_storage()));
230 }
231
232 #[test]
233 #[cfg_attr(kani, kani::proof)]
234 fn u24_new_truncated() {
235 bolero::check!()
236 .with_type()
237 .cloned()
238 .for_each(|value: u32| Some(u24::new_truncated(value)));
239 }
240
241 #[test]
242 #[cfg_attr(kani, kani::proof)]
243 fn u24_ref_to_be_bytes() {
244 bolero::check!()
245 .with_type()
246 .for_each(|callee: &u24| Some(callee.to_be_bytes()));
247 }
248
249 #[test]
250 #[cfg_attr(kani, kani::proof)]
251 fn u32_try_from() {
252 bolero::check!()
253 .with_type()
254 .cloned()
255 .for_each(|value: u32| Some(u24::try_from(value)));
256 }
257
258 #[test]
259 #[cfg_attr(kani, kani::proof)]
260 fn u8_from() {
261 bolero::check!()
262 .with_type()
263 .cloned()
264 .for_each(|value: u8| Some(u24::from(value)));
265 }
266
267 #[test]
268 #[cfg_attr(kani, kani::proof)]
269 fn u16_from() {
270 bolero::check!()
271 .with_type()
272 .cloned()
273 .for_each(|value: u16| Some(u24::from(value)));
274 }
275
276 #[test]
277 #[cfg_attr(kani, kani::proof)]
278 fn u24_deref() {
279 bolero::check!()
280 .with_type()
281 .cloned()
282 .for_each(|callee: u24| Some(*callee.deref()));
283 }
284
285 #[test]
286 #[cfg_attr(kani, kani::proof)]
287 fn i32_new_truncated() {
288 bolero::check!()
289 .with_type()
290 .cloned()
291 .for_each(|value: i32| Some(i24::new_truncated(value)));
292 }
293
294 #[test]
295 #[cfg_attr(kani, kani::proof)]
296 fn i24_to_be_bytes() {
297 bolero::check!()
298 .with_type()
299 .cloned()
300 .for_each(|callee: i24| Some(callee.to_be_bytes()));
301 }
302
303 #[test]
304 #[cfg_attr(kani, kani::proof)]
305 fn i32_try_from() {
306 bolero::check!()
307 .with_type()
308 .cloned()
309 .for_each(|value: i32| Some(i24::try_from(value)));
310 }
311
312 #[test]
313 #[cfg_attr(kani, kani::proof)]
314 fn i24_from_u8() {
315 bolero::check!()
316 .with_type()
317 .cloned()
318 .for_each(|value: u8| Some(i24::from(value)));
319 }
320
321 #[test]
322 #[cfg_attr(kani, kani::proof)]
323 fn i8_from() {
324 bolero::check!()
325 .with_type()
326 .cloned()
327 .for_each(|value: i8| Some(i24::from(value)));
328 }
329
330 #[test]
331 #[cfg_attr(kani, kani::proof)]
332 fn i24_from_u16() {
333 bolero::check!()
334 .with_type()
335 .cloned()
336 .for_each(|value: u16| Some(i24::from(value)));
337 }
338
339 #[test]
340 #[cfg_attr(kani, kani::proof)]
341 fn i16_from() {
342 bolero::check!()
343 .with_type()
344 .cloned()
345 .for_each(|value: i16| Some(i24::from(value)));
346 }
347
348 #[test]
349 #[cfg_attr(kani, kani::proof)]
350 fn i24_deref() {
351 bolero::check!()
352 .with_type()
353 .cloned()
354 .for_each(|callee: i24| Some(*callee.deref()));
355 }
356
357 #[test]
358 #[cfg_attr(kani, kani::proof)]
359 fn u64_try_from() {
360 bolero::check!()
361 .with_type()
362 .cloned()
363 .for_each(|value: u64| Some(u24::try_from(value)));
364 }
365
366 #[test]
367 #[cfg_attr(kani, kani::proof)]
368 fn u64_new_truncated() {
369 bolero::check!()
370 .with_type()
371 .cloned()
372 .for_each(|value: u64| Some(u48::new_truncated(value)));
373 }
374
375 #[test]
376 #[cfg_attr(kani, kani::proof)]
377 fn u48_to_be_bytes() {
378 bolero::check!()
379 .with_type()
380 .cloned()
381 .for_each(|callee: u48| Some(callee.to_be_bytes()));
382 }
383
384 #[test]
385 #[cfg_attr(kani, kani::proof)]
386 fn u48_try_from_u64() {
387 bolero::check!()
388 .with_type()
389 .cloned()
390 .for_each(|value: u64| Some(u48::try_from(value)));
391 }
392
393 #[test]
394 #[cfg_attr(kani, kani::proof)]
395 fn u8_from_u8() {
396 bolero::check!()
397 .with_type()
398 .cloned()
399 .for_each(|value: u8| Some(u48::from(value)));
400 }
401
402 #[test]
403 #[cfg_attr(kani, kani::proof)]
404 fn u48_from_u16() {
405 bolero::check!()
406 .with_type()
407 .cloned()
408 .for_each(|value: u16| Some(u48::from(value)));
409 }
410
411 #[test]
412 #[cfg_attr(kani, kani::proof)]
413 fn u32_from() {
414 bolero::check!()
415 .with_type()
416 .cloned()
417 .for_each(|value: u32| Some(u48::from(value)));
418 }
419
420 #[test]
421 #[cfg_attr(kani, kani::proof)]
422 fn u48_deref() {
423 bolero::check!()
424 .with_type()
425 .cloned()
426 .for_each(|callee: u48| Some(*callee.deref()));
427 }
428
429 #[test]
430 #[cfg_attr(kani, kani::proof)]
431 fn i64_new_truncated() {
432 bolero::check!()
433 .with_type()
434 .cloned()
435 .for_each(|value: i64| Some(i48::new_truncated(value)));
436 }
437
438 #[test]
439 #[cfg_attr(kani, kani::proof)]
440 fn i48_to_be_bytes() {
441 bolero::check!()
442 .with_type()
443 .cloned()
444 .for_each(|callee: i48| Some(callee.to_be_bytes()));
445 }
446
447 #[test]
448 #[cfg_attr(kani, kani::proof)]
449 fn i64_try_from() {
450 bolero::check!()
451 .with_type()
452 .cloned()
453 .for_each(|value: i64| Some(i48::try_from(value)));
454 }
455
456 #[test]
457 #[cfg_attr(kani, kani::proof)]
458 fn i48_from_u8() {
459 bolero::check!()
460 .with_type()
461 .cloned()
462 .for_each(|value: u8| Some(i48::from(value)));
463 }
464
465 #[test]
466 #[cfg_attr(kani, kani::proof)]
467 fn i48_from_i8() {
468 bolero::check!()
469 .with_type()
470 .cloned()
471 .for_each(|value: i8| Some(i48::from(value)));
472 }
473
474 #[test]
475 #[cfg_attr(kani, kani::proof)]
476 fn i48_from_i48() {
477 bolero::check!()
478 .with_type()
479 .cloned()
480 .for_each(|value: u16| Some(i48::from(value)));
481 }
482
483 #[test]
484 #[cfg_attr(kani, kani::proof)]
485 fn i48_from_i16() {
486 bolero::check!()
487 .with_type()
488 .cloned()
489 .for_each(|value: i16| Some(i48::from(value)));
490 }
491
492 #[test]
493 #[cfg_attr(kani, kani::proof)]
494 fn i48_from_u32() {
495 bolero::check!()
496 .with_type()
497 .cloned()
498 .for_each(|value: u32| Some(i48::from(value)));
499 }
500
501 #[test]
502 #[cfg_attr(kani, kani::proof)]
503 fn i32_from() {
504 bolero::check!()
505 .with_type()
506 .cloned()
507 .for_each(|value: i32| Some(i48::from(value)));
508 }
509
510 #[test]
511 #[cfg_attr(kani, kani::proof)]
512 fn i48_deref() {
513 bolero::check!()
514 .with_type()
515 .cloned()
516 .for_each(|callee: i48| Some(*callee.deref()));
517 }
518}