1use core::{
71 convert::{TryFrom, TryInto},
72 fmt::{self, Binary, Debug, Display, Formatter, LowerHex, Octal, UpperHex},
73 marker::PhantomData,
74 num::TryFromIntError,
75};
76
77pub use ::byteorder::{BigEndian, ByteOrder, LittleEndian, NativeEndian, NetworkEndian, BE, LE};
81
82use super::*;
83
84macro_rules! impl_fmt_trait {
85 ($name:ident, $native:ident, $trait:ident) => {
86 impl<O: ByteOrder> $trait for $name<O> {
87 #[inline(always)]
88 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
89 $trait::fmt(&self.get(), f)
90 }
91 }
92 };
93}
94
95macro_rules! impl_fmt_traits {
96 ($name:ident, $native:ident, "floating point number") => {
97 impl_fmt_trait!($name, $native, Display);
98 };
99 ($name:ident, $native:ident, "unsigned integer") => {
100 impl_fmt_traits!($name, $native, @all_types);
101 };
102 ($name:ident, $native:ident, "signed integer") => {
103 impl_fmt_traits!($name, $native, @all_types);
104 };
105 ($name:ident, $native:ident, @all_types) => {
106 impl_fmt_trait!($name, $native, Display);
107 impl_fmt_trait!($name, $native, Octal);
108 impl_fmt_trait!($name, $native, LowerHex);
109 impl_fmt_trait!($name, $native, UpperHex);
110 impl_fmt_trait!($name, $native, Binary);
111 };
112}
113
114macro_rules! impl_ops_traits {
115 ($name:ident, $native:ident, "floating point number") => {
116 impl_ops_traits!($name, $native, @all_types);
117 impl_ops_traits!($name, $native, @signed_integer_floating_point);
118 };
119 ($name:ident, $native:ident, "unsigned integer") => {
120 impl_ops_traits!($name, $native, @signed_unsigned_integer);
121 impl_ops_traits!($name, $native, @all_types);
122 };
123 ($name:ident, $native:ident, "signed integer") => {
124 impl_ops_traits!($name, $native, @signed_unsigned_integer);
125 impl_ops_traits!($name, $native, @signed_integer_floating_point);
126 impl_ops_traits!($name, $native, @all_types);
127 };
128 ($name:ident, $native:ident, @signed_unsigned_integer) => {
129 impl_ops_traits!(@without_byteorder_swap $name, $native, BitAnd, bitand, BitAndAssign, bitand_assign);
130 impl_ops_traits!(@without_byteorder_swap $name, $native, BitOr, bitor, BitOrAssign, bitor_assign);
131 impl_ops_traits!(@without_byteorder_swap $name, $native, BitXor, bitxor, BitXorAssign, bitxor_assign);
132 impl_ops_traits!(@with_byteorder_swap $name, $native, Shl, shl, ShlAssign, shl_assign);
133 impl_ops_traits!(@with_byteorder_swap $name, $native, Shr, shr, ShrAssign, shr_assign);
134
135 impl<O> core::ops::Not for $name<O> {
136 type Output = $name<O>;
137
138 #[inline(always)]
139 fn not(self) -> $name<O> {
140 let self_native = $native::from_ne_bytes(self.0);
141 $name((!self_native).to_ne_bytes(), PhantomData)
142 }
143 }
144 };
145 ($name:ident, $native:ident, @signed_integer_floating_point) => {
146 impl<O: ByteOrder> core::ops::Neg for $name<O> {
147 type Output = $name<O>;
148
149 #[inline(always)]
150 fn neg(self) -> $name<O> {
151 let self_native: $native = self.get();
152 #[allow(clippy::arithmetic_side_effects)]
153 $name::<O>::new(-self_native)
154 }
155 }
156 };
157 ($name:ident, $native:ident, @all_types) => {
158 impl_ops_traits!(@with_byteorder_swap $name, $native, Add, add, AddAssign, add_assign);
159 impl_ops_traits!(@with_byteorder_swap $name, $native, Div, div, DivAssign, div_assign);
160 impl_ops_traits!(@with_byteorder_swap $name, $native, Mul, mul, MulAssign, mul_assign);
161 impl_ops_traits!(@with_byteorder_swap $name, $native, Rem, rem, RemAssign, rem_assign);
162 impl_ops_traits!(@with_byteorder_swap $name, $native, Sub, sub, SubAssign, sub_assign);
163 };
164 (@with_byteorder_swap $name:ident, $native:ident, $trait:ident, $method:ident, $trait_assign:ident, $method_assign:ident) => {
165 impl<O: ByteOrder> core::ops::$trait for $name<O> {
166 type Output = $name<O>;
167
168 #[inline(always)]
169 fn $method(self, rhs: $name<O>) -> $name<O> {
170 let self_native: $native = self.get();
171 let rhs_native: $native = rhs.get();
172 let result_native = core::ops::$trait::$method(self_native, rhs_native);
173 $name::<O>::new(result_native)
174 }
175 }
176
177 impl<O: ByteOrder> core::ops::$trait_assign for $name<O> {
178 #[inline(always)]
179 fn $method_assign(&mut self, rhs: $name<O>) {
180 *self = core::ops::$trait::$method(*self, rhs);
181 }
182 }
183 };
184 (@without_byteorder_swap $name:ident, $native:ident, $trait:ident, $method:ident, $trait_assign:ident, $method_assign:ident) => {
188 impl<O: ByteOrder> core::ops::$trait for $name<O> {
189 type Output = $name<O>;
190
191 #[inline(always)]
192 fn $method(self, rhs: $name<O>) -> $name<O> {
193 let self_native = $native::from_ne_bytes(self.0);
194 let rhs_native = $native::from_ne_bytes(rhs.0);
195 let result_native = core::ops::$trait::$method(self_native, rhs_native);
196 $name(result_native.to_ne_bytes(), PhantomData)
197 }
198 }
199
200 impl<O: ByteOrder> core::ops::$trait_assign for $name<O> {
201 #[inline(always)]
202 fn $method_assign(&mut self, rhs: $name<O>) {
203 *self = core::ops::$trait::$method(*self, rhs);
204 }
205 }
206 };
207}
208
209macro_rules! doc_comment {
210 ($x:expr, $($tt:tt)*) => {
211 #[doc = $x]
212 $($tt)*
213 };
214}
215
216macro_rules! define_max_value_constant {
217 ($name:ident, $bytes:expr, "unsigned integer") => {
218 pub const MAX_VALUE: $name<O> = $name([0xFFu8; $bytes], PhantomData);
224 };
225 ($name:ident, $bytes:expr, "signed integer") => {};
233 ($name:ident, $bytes:expr, "floating point number") => {};
234}
235
236macro_rules! define_type {
237 ($article:ident,
238 $name:ident,
239 $native:ident,
240 $bits:expr,
241 $bytes:expr,
242 $read_method:ident,
243 $write_method:ident,
244 $number_kind:tt,
245 [$($larger_native:ty),*],
246 [$($larger_native_try:ty),*],
247 [$($larger_byteorder:ident),*],
248 [$($larger_byteorder_try:ident),*]) => {
249 doc_comment! {
250 concat!("A ", stringify!($bits), "-bit ", $number_kind,
251 " stored in a given byte order.
252
253`", stringify!($name), "` is like the native `", stringify!($native), "` type with
254two major differences: First, it has no alignment requirement (its alignment is 1).
255Second, the endianness of its memory layout is given by the type parameter `O`,
256which can be any type which implements [`ByteOrder`]. In particular, this refers
257to [`BigEndian`], [`LittleEndian`], [`NativeEndian`], and [`NetworkEndian`].
258
259", stringify!($article), " `", stringify!($name), "` can be constructed using
260the [`new`] method, and its contained value can be obtained as a native
261`",stringify!($native), "` using the [`get`] method, or updated in place with
262the [`set`] method. In all cases, if the endianness `O` is not the same as the
263endianness of the current platform, an endianness swap will be performed in
264order to uphold the invariants that a) the layout of `", stringify!($name), "`
265has endianness `O` and that, b) the layout of `", stringify!($native), "` has
266the platform's native endianness.
267
268`", stringify!($name), "` implements [`FromBytes`], [`AsBytes`], and [`Unaligned`],
269making it useful for parsing and serialization. See the module documentation for an
270example of how it can be used for parsing UDP packets.
271
272[`new`]: crate::byteorder::", stringify!($name), "::new
273[`get`]: crate::byteorder::", stringify!($name), "::get
274[`set`]: crate::byteorder::", stringify!($name), "::set
275[`FromBytes`]: crate::FromBytes
276[`AsBytes`]: crate::AsBytes
277[`Unaligned`]: crate::Unaligned"),
278 #[derive(Copy, Clone, Eq, PartialEq, Hash)]
279 #[cfg_attr(any(feature = "derive", test), derive(KnownLayout, FromZeroes, FromBytes, AsBytes, Unaligned))]
280 #[repr(transparent)]
281 pub struct $name<O>([u8; $bytes], PhantomData<O>);
282 }
283
284 #[cfg(not(any(feature = "derive", test)))]
285 impl_known_layout!(O => $name<O>);
286
287 safety_comment! {
288 impl_or_verify!(O => FromZeroes for $name<O>);
293 impl_or_verify!(O => FromBytes for $name<O>);
294 impl_or_verify!(O => AsBytes for $name<O>);
295 impl_or_verify!(O => Unaligned for $name<O>);
296 }
297
298 impl<O> Default for $name<O> {
299 #[inline(always)]
300 fn default() -> $name<O> {
301 $name::ZERO
302 }
303 }
304
305 impl<O> $name<O> {
306 pub const ZERO: $name<O> = $name([0u8; $bytes], PhantomData);
312
313 define_max_value_constant!($name, $bytes, $number_kind);
314
315 #[inline(always)]
318 pub const fn from_bytes(bytes: [u8; $bytes]) -> $name<O> {
319 $name(bytes, PhantomData)
320 }
321 }
322
323 impl<O: ByteOrder> $name<O> {
324 #[inline(always)]
330 pub fn new(n: $native) -> $name<O> {
331 let mut out = $name::default();
332 O::$write_method(&mut out.0[..], n);
333 out
334 }
335
336 #[inline(always)]
340 pub fn get(self) -> $native {
341 O::$read_method(&self.0[..])
342 }
343
344 #[inline(always)]
348 pub fn set(&mut self, n: $native) {
349 O::$write_method(&mut self.0[..], n);
350 }
351 }
352
353 impl<O: ByteOrder> From<$name<O>> for [u8; $bytes] {
359 #[inline(always)]
360 fn from(x: $name<O>) -> [u8; $bytes] {
361 x.0
362 }
363 }
364
365 impl<O: ByteOrder> From<[u8; $bytes]> for $name<O> {
366 #[inline(always)]
367 fn from(bytes: [u8; $bytes]) -> $name<O> {
368 $name(bytes, PhantomData)
369 }
370 }
371
372 impl<O: ByteOrder> From<$name<O>> for $native {
373 #[inline(always)]
374 fn from(x: $name<O>) -> $native {
375 x.get()
376 }
377 }
378
379 impl<O: ByteOrder> From<$native> for $name<O> {
380 #[inline(always)]
381 fn from(x: $native) -> $name<O> {
382 $name::new(x)
383 }
384 }
385
386 $(
387 impl<O: ByteOrder> From<$name<O>> for $larger_native {
388 #[inline(always)]
389 fn from(x: $name<O>) -> $larger_native {
390 x.get().into()
391 }
392 }
393 )*
394
395 $(
396 impl<O: ByteOrder> TryFrom<$larger_native_try> for $name<O> {
397 type Error = TryFromIntError;
398 #[inline(always)]
399 fn try_from(x: $larger_native_try) -> Result<$name<O>, TryFromIntError> {
400 $native::try_from(x).map($name::new)
401 }
402 }
403 )*
404
405 $(
406 impl<O: ByteOrder, P: ByteOrder> From<$name<O>> for $larger_byteorder<P> {
407 #[inline(always)]
408 fn from(x: $name<O>) -> $larger_byteorder<P> {
409 $larger_byteorder::new(x.get().into())
410 }
411 }
412 )*
413
414 $(
415 impl<O: ByteOrder, P: ByteOrder> TryFrom<$larger_byteorder_try<P>> for $name<O> {
416 type Error = TryFromIntError;
417 #[inline(always)]
418 fn try_from(x: $larger_byteorder_try<P>) -> Result<$name<O>, TryFromIntError> {
419 x.get().try_into().map($name::new)
420 }
421 }
422 )*
423
424 impl<O: ByteOrder> AsRef<[u8; $bytes]> for $name<O> {
425 #[inline(always)]
426 fn as_ref(&self) -> &[u8; $bytes] {
427 &self.0
428 }
429 }
430
431 impl<O: ByteOrder> AsMut<[u8; $bytes]> for $name<O> {
432 #[inline(always)]
433 fn as_mut(&mut self) -> &mut [u8; $bytes] {
434 &mut self.0
435 }
436 }
437
438 impl<O: ByteOrder> PartialEq<$name<O>> for [u8; $bytes] {
439 #[inline(always)]
440 fn eq(&self, other: &$name<O>) -> bool {
441 self.eq(&other.0)
442 }
443 }
444
445 impl<O: ByteOrder> PartialEq<[u8; $bytes]> for $name<O> {
446 #[inline(always)]
447 fn eq(&self, other: &[u8; $bytes]) -> bool {
448 self.0.eq(other)
449 }
450 }
451
452 impl_fmt_traits!($name, $native, $number_kind);
453 impl_ops_traits!($name, $native, $number_kind);
454
455 impl<O: ByteOrder> Debug for $name<O> {
456 #[inline]
457 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
458 f.debug_tuple(stringify!($name)).field(&self.get()).finish()
460 }
461 }
462 };
463}
464
465define_type!(
466 A,
467 U16,
468 u16,
469 16,
470 2,
471 read_u16,
472 write_u16,
473 "unsigned integer",
474 [u32, u64, u128, usize],
475 [u32, u64, u128, usize],
476 [U32, U64, U128],
477 [U32, U64, U128]
478);
479define_type!(
480 A,
481 U32,
482 u32,
483 32,
484 4,
485 read_u32,
486 write_u32,
487 "unsigned integer",
488 [u64, u128],
489 [u64, u128],
490 [U64, U128],
491 [U64, U128]
492);
493define_type!(
494 A,
495 U64,
496 u64,
497 64,
498 8,
499 read_u64,
500 write_u64,
501 "unsigned integer",
502 [u128],
503 [u128],
504 [U128],
505 [U128]
506);
507define_type!(A, U128, u128, 128, 16, read_u128, write_u128, "unsigned integer", [], [], [], []);
508define_type!(
509 An,
510 I16,
511 i16,
512 16,
513 2,
514 read_i16,
515 write_i16,
516 "signed integer",
517 [i32, i64, i128, isize],
518 [i32, i64, i128, isize],
519 [I32, I64, I128],
520 [I32, I64, I128]
521);
522define_type!(
523 An,
524 I32,
525 i32,
526 32,
527 4,
528 read_i32,
529 write_i32,
530 "signed integer",
531 [i64, i128],
532 [i64, i128],
533 [I64, I128],
534 [I64, I128]
535);
536define_type!(
537 An,
538 I64,
539 i64,
540 64,
541 8,
542 read_i64,
543 write_i64,
544 "signed integer",
545 [i128],
546 [i128],
547 [I128],
548 [I128]
549);
550define_type!(An, I128, i128, 128, 16, read_i128, write_i128, "signed integer", [], [], [], []);
551define_type!(
552 An,
553 F32,
554 f32,
555 32,
556 4,
557 read_f32,
558 write_f32,
559 "floating point number",
560 [f64],
561 [],
562 [F64],
563 []
564);
565define_type!(An, F64, f64, 64, 8, read_f64, write_f64, "floating point number", [], [], [], []);
566
567macro_rules! module {
568 ($name:ident, $trait:ident, $endianness_str:expr) => {
569 #[doc = $endianness_str]
571 pub mod $name {
573 use byteorder::$trait;
574
575 module!(@ty U16, $trait, "16-bit unsigned integer", $endianness_str);
576 module!(@ty U32, $trait, "32-bit unsigned integer", $endianness_str);
577 module!(@ty U64, $trait, "64-bit unsigned integer", $endianness_str);
578 module!(@ty U128, $trait, "128-bit unsigned integer", $endianness_str);
579 module!(@ty I16, $trait, "16-bit signed integer", $endianness_str);
580 module!(@ty I32, $trait, "32-bit signed integer", $endianness_str);
581 module!(@ty I64, $trait, "64-bit signed integer", $endianness_str);
582 module!(@ty I128, $trait, "128-bit signed integer", $endianness_str);
583 module!(@ty F32, $trait, "32-bit floating point number", $endianness_str);
584 module!(@ty F64, $trait, "64-bit floating point number", $endianness_str);
585 }
586 };
587 (@ty $ty:ident, $trait:ident, $desc_str:expr, $endianness_str:expr) => {
588 #[doc = $desc_str]
590 #[doc = $endianness_str]
592 pub type $ty = crate::byteorder::$ty<$trait>;
594 };
595}
596
597module!(big_endian, BigEndian, "big-endian");
598module!(little_endian, LittleEndian, "little-endian");
599module!(network_endian, NetworkEndian, "network-endian");
600module!(native_endian, NativeEndian, "native-endian");
601
602#[cfg(any(test, kani))]
603mod tests {
604 use ::byteorder::NativeEndian;
605
606 use {
607 super::*,
608 crate::{AsBytes, FromBytes, Unaligned},
609 };
610
611 #[cfg(not(kani))]
612 mod compatibility {
613 pub(super) use rand::{
614 distributions::{Distribution, Standard},
615 rngs::SmallRng,
616 Rng, SeedableRng,
617 };
618
619 pub(crate) trait Arbitrary {}
620
621 impl<T> Arbitrary for T {}
622 }
623
624 #[cfg(kani)]
625 mod compatibility {
626 pub(crate) use kani::Arbitrary;
627
628 pub(crate) struct SmallRng;
629
630 impl SmallRng {
631 pub(crate) fn seed_from_u64(_state: u64) -> Self {
632 Self
633 }
634 }
635
636 pub(crate) trait Rng {
637 fn sample<T, D: Distribution<T>>(&mut self, _distr: D) -> T
638 where
639 T: Arbitrary,
640 {
641 kani::any()
642 }
643 }
644
645 impl Rng for SmallRng {}
646
647 pub(crate) trait Distribution<T> {}
648 impl<T, U> Distribution<T> for U {}
649
650 pub(crate) struct Standard;
651 }
652
653 use compatibility::*;
654
655 #[cfg_attr(kani, allow(dead_code))]
657 trait Native: Arbitrary + FromBytes + AsBytes + Copy + PartialEq + Debug {
658 const ZERO: Self;
659 const MAX_VALUE: Self;
660
661 type Distribution: Distribution<Self>;
662 const DIST: Self::Distribution;
663
664 fn rand<R: Rng>(rng: &mut R) -> Self {
665 rng.sample(Self::DIST)
666 }
667
668 fn checked_add(self, rhs: Self) -> Option<Self>;
669 fn checked_div(self, rhs: Self) -> Option<Self>;
670 fn checked_mul(self, rhs: Self) -> Option<Self>;
671 fn checked_rem(self, rhs: Self) -> Option<Self>;
672 fn checked_sub(self, rhs: Self) -> Option<Self>;
673 fn checked_shl(self, rhs: Self) -> Option<Self>;
674 fn checked_shr(self, rhs: Self) -> Option<Self>;
675
676 fn is_nan(self) -> bool;
677
678 fn assert_eq_or_nan(self, other: Self) {
682 let slf = (!self.is_nan()).then(|| self);
683 let other = (!other.is_nan()).then(|| other);
684 assert_eq!(slf, other);
685 }
686 }
687
688 trait ByteArray:
689 FromBytes + AsBytes + Copy + AsRef<[u8]> + AsMut<[u8]> + Debug + Default + Eq
690 {
691 fn invert(self) -> Self;
693 }
694
695 trait ByteOrderType: FromBytes + AsBytes + Unaligned + Copy + Eq + Debug {
696 type Native: Native;
697 type ByteArray: ByteArray;
698
699 const ZERO: Self;
700
701 fn new(native: Self::Native) -> Self;
702 fn get(self) -> Self::Native;
703 fn set(&mut self, native: Self::Native);
704 fn from_bytes(bytes: Self::ByteArray) -> Self;
705 fn into_bytes(self) -> Self::ByteArray;
706
707 fn assert_eq_or_nan(self, other: Self) {
711 let slf = (!self.get().is_nan()).then(|| self);
712 let other = (!other.get().is_nan()).then(|| other);
713 assert_eq!(slf, other);
714 }
715 }
716
717 trait ByteOrderTypeUnsigned: ByteOrderType {
718 const MAX_VALUE: Self;
719 }
720
721 macro_rules! impl_byte_array {
722 ($bytes:expr) => {
723 impl ByteArray for [u8; $bytes] {
724 fn invert(mut self) -> [u8; $bytes] {
725 self.reverse();
726 self
727 }
728 }
729 };
730 }
731
732 impl_byte_array!(2);
733 impl_byte_array!(4);
734 impl_byte_array!(8);
735 impl_byte_array!(16);
736
737 macro_rules! impl_byte_order_type_unsigned {
738 ($name:ident, unsigned) => {
739 impl<O: ByteOrder> ByteOrderTypeUnsigned for $name<O> {
740 const MAX_VALUE: $name<O> = $name::MAX_VALUE;
741 }
742 };
743 ($name:ident, signed) => {};
744 }
745
746 macro_rules! impl_traits {
747 ($name:ident, $native:ident, $bytes:expr, $sign:ident $(, @$float:ident)?) => {
748 impl Native for $native {
749 #[allow(trivial_numeric_casts, clippy::as_conversions)]
754 const ZERO: $native = 0 as $native;
755 const MAX_VALUE: $native = $native::MAX;
756
757 type Distribution = Standard;
758 const DIST: Standard = Standard;
759
760 impl_traits!(@float_dependent_methods $(@$float)?);
761 }
762
763 impl<O: ByteOrder> ByteOrderType for $name<O> {
764 type Native = $native;
765 type ByteArray = [u8; $bytes];
766
767 const ZERO: $name<O> = $name::ZERO;
768
769 fn new(native: $native) -> $name<O> {
770 $name::new(native)
771 }
772
773 fn get(self) -> $native {
774 $name::get(self)
775 }
776
777 fn set(&mut self, native: $native) {
778 $name::set(self, native)
779 }
780
781 fn from_bytes(bytes: [u8; $bytes]) -> $name<O> {
782 $name::from(bytes)
783 }
784
785 fn into_bytes(self) -> [u8; $bytes] {
786 <[u8; $bytes]>::from(self)
787 }
788 }
789
790 impl_byte_order_type_unsigned!($name, $sign);
791 };
792 (@float_dependent_methods) => {
793 fn checked_add(self, rhs: Self) -> Option<Self> { self.checked_add(rhs) }
794 fn checked_div(self, rhs: Self) -> Option<Self> { self.checked_div(rhs) }
795 fn checked_mul(self, rhs: Self) -> Option<Self> { self.checked_mul(rhs) }
796 fn checked_rem(self, rhs: Self) -> Option<Self> { self.checked_rem(rhs) }
797 fn checked_sub(self, rhs: Self) -> Option<Self> { self.checked_sub(rhs) }
798 fn checked_shl(self, rhs: Self) -> Option<Self> { self.checked_shl(rhs.try_into().unwrap_or(u32::MAX)) }
799 fn checked_shr(self, rhs: Self) -> Option<Self> { self.checked_shr(rhs.try_into().unwrap_or(u32::MAX)) }
800 fn is_nan(self) -> bool { false }
801 };
802 (@float_dependent_methods @float) => {
803 fn checked_add(self, rhs: Self) -> Option<Self> { Some(self + rhs) }
804 fn checked_div(self, rhs: Self) -> Option<Self> { Some(self / rhs) }
805 fn checked_mul(self, rhs: Self) -> Option<Self> { Some(self * rhs) }
806 fn checked_rem(self, rhs: Self) -> Option<Self> { Some(self % rhs) }
807 fn checked_sub(self, rhs: Self) -> Option<Self> { Some(self - rhs) }
808 fn checked_shl(self, _rhs: Self) -> Option<Self> { unimplemented!() }
809 fn checked_shr(self, _rhs: Self) -> Option<Self> { unimplemented!() }
810 fn is_nan(self) -> bool { self.is_nan() }
811 };
812 }
813
814 impl_traits!(U16, u16, 2, unsigned);
815 impl_traits!(U32, u32, 4, unsigned);
816 impl_traits!(U64, u64, 8, unsigned);
817 impl_traits!(U128, u128, 16, unsigned);
818 impl_traits!(I16, i16, 2, signed);
819 impl_traits!(I32, i32, 4, signed);
820 impl_traits!(I64, i64, 8, signed);
821 impl_traits!(I128, i128, 16, signed);
822 impl_traits!(F32, f32, 4, signed, @float);
823 impl_traits!(F64, f64, 8, signed, @float);
824
825 macro_rules! call_for_unsigned_types {
826 ($fn:ident, $byteorder:ident) => {
827 $fn::<U16<$byteorder>>();
828 $fn::<U32<$byteorder>>();
829 $fn::<U64<$byteorder>>();
830 $fn::<U128<$byteorder>>();
831 };
832 }
833
834 macro_rules! call_for_signed_types {
835 ($fn:ident, $byteorder:ident) => {
836 $fn::<I16<$byteorder>>();
837 $fn::<I32<$byteorder>>();
838 $fn::<I64<$byteorder>>();
839 $fn::<I128<$byteorder>>();
840 };
841 }
842
843 macro_rules! call_for_float_types {
844 ($fn:ident, $byteorder:ident) => {
845 $fn::<F32<$byteorder>>();
846 $fn::<F64<$byteorder>>();
847 };
848 }
849
850 macro_rules! call_for_all_types {
851 ($fn:ident, $byteorder:ident) => {
852 call_for_unsigned_types!($fn, $byteorder);
853 call_for_signed_types!($fn, $byteorder);
854 call_for_float_types!($fn, $byteorder);
855 };
856 }
857
858 #[cfg(target_endian = "big")]
859 type NonNativeEndian = LittleEndian;
860 #[cfg(target_endian = "little")]
861 type NonNativeEndian = BigEndian;
862
863 const RNG_SEED: u64 = 0x7A03CAE2F32B5B8F;
868
869 const RAND_ITERS: usize = if cfg!(any(miri, kani)) {
870 1
889 } else {
890 1024
891 };
892
893 #[cfg_attr(test, test)]
894 #[cfg_attr(kani, kani::proof)]
895 fn test_zero() {
896 fn test_zero<T: ByteOrderType>() {
897 assert_eq!(T::ZERO.get(), T::Native::ZERO);
898 }
899
900 call_for_all_types!(test_zero, NativeEndian);
901 call_for_all_types!(test_zero, NonNativeEndian);
902 }
903
904 #[cfg_attr(test, test)]
905 #[cfg_attr(kani, kani::proof)]
906 fn test_max_value() {
907 fn test_max_value<T: ByteOrderTypeUnsigned>() {
908 assert_eq!(T::MAX_VALUE.get(), T::Native::MAX_VALUE);
909 }
910
911 call_for_unsigned_types!(test_max_value, NativeEndian);
912 call_for_unsigned_types!(test_max_value, NonNativeEndian);
913 }
914
915 #[cfg_attr(test, test)]
916 #[cfg_attr(kani, kani::proof)]
917 fn test_endian() {
918 fn test<T: ByteOrderType>(invert: bool) {
919 let mut r = SmallRng::seed_from_u64(RNG_SEED);
920 for _ in 0..RAND_ITERS {
921 let native = T::Native::rand(&mut r);
922 let mut bytes = T::ByteArray::default();
923 bytes.as_bytes_mut().copy_from_slice(native.as_bytes());
924 if invert {
925 bytes = bytes.invert();
926 }
927 let mut from_native = T::new(native);
928 let from_bytes = T::from_bytes(bytes);
929
930 from_native.assert_eq_or_nan(from_bytes);
931 from_native.get().assert_eq_or_nan(native);
932 from_bytes.get().assert_eq_or_nan(native);
933
934 assert_eq!(from_native.into_bytes(), bytes);
935 assert_eq!(from_bytes.into_bytes(), bytes);
936
937 let updated = T::Native::rand(&mut r);
938 from_native.set(updated);
939 from_native.get().assert_eq_or_nan(updated);
940 }
941 }
942
943 fn test_native<T: ByteOrderType>() {
944 test::<T>(false);
945 }
946
947 fn test_non_native<T: ByteOrderType>() {
948 test::<T>(true);
949 }
950
951 call_for_all_types!(test_native, NativeEndian);
952 call_for_all_types!(test_non_native, NonNativeEndian);
953 }
954
955 #[test]
956 fn test_ops_impls() {
957 fn test<T, F, G, H>(op: F, op_native: G, op_native_checked: Option<H>)
964 where
965 T: ByteOrderType,
966 F: Fn(T, T) -> T,
967 G: Fn(T::Native, T::Native) -> T::Native,
968 H: Fn(T::Native, T::Native) -> Option<T::Native>,
969 {
970 let mut r = SmallRng::seed_from_u64(RNG_SEED);
971 for _ in 0..RAND_ITERS {
972 let n0 = T::Native::rand(&mut r);
973 let n1 = T::Native::rand(&mut r);
974 let t0 = T::new(n0);
975 let t1 = T::new(n1);
976
977 if matches!(&op_native_checked, Some(checked) if checked(n0, n1).is_none()) {
980 continue;
981 }
982
983 let n_res = op_native(n0, n1);
984 let t_res = op(t0, t1);
985
986 let n_res = (!T::Native::is_nan(n_res)).then(|| n_res);
990 let t_res = (!T::Native::is_nan(t_res.get())).then(|| t_res.get());
991 assert_eq!(n_res, t_res);
992 }
993 }
994
995 macro_rules! test {
996 (@binary $trait:ident, $method:ident $([$checked_method:ident])?, $($call_for_macros:ident),*) => {{
997 test!(
998 @inner $trait,
999 core::ops::$trait::$method,
1000 core::ops::$trait::$method,
1001 {
1002 #[allow(unused_mut, unused_assignments)]
1003 let mut op_native_checked = None::<fn(T::Native, T::Native) -> Option<T::Native>>;
1004 $(
1005 op_native_checked = Some(T::Native::$checked_method);
1006 )?
1007 op_native_checked
1008 },
1009 $($call_for_macros),*
1010 );
1011 }};
1012 (@unary $trait:ident, $method:ident $([$checked_method:ident])?, $($call_for_macros:ident),*) => {{
1013 test!(
1014 @inner $trait,
1015 |slf, _rhs| core::ops::$trait::$method(slf),
1016 |slf, _rhs| core::ops::$trait::$method(slf),
1017 {
1018 #[allow(unused_mut, unused_assignments)]
1019 let mut op_native_checked = None::<fn(T::Native, T::Native) -> Option<T::Native>>;
1020 $(
1021 op_native_checked = Some(|slf, _rhs| T::Native::$checked_method(slf));
1022 )?
1023 op_native_checked
1024 },
1025 $($call_for_macros),*
1026 );
1027 }};
1028 (@inner $trait:ident, $op:expr, $op_native:expr, $op_native_checked:expr, $($call_for_macros:ident),*) => {{
1029 fn t<T: ByteOrderType + core::ops::$trait<Output = T>>()
1030 where
1031 T::Native: core::ops::$trait<Output = T::Native>,
1032 {
1033 test::<T, _, _, _>(
1034 $op,
1035 $op_native,
1036 $op_native_checked,
1037 );
1038 }
1039
1040 $(
1041 $call_for_macros!(t, NativeEndian);
1042 $call_for_macros!(t, NonNativeEndian);
1043 )*
1044 }};
1045 }
1046
1047 test!(@binary Add, add[checked_add], call_for_all_types);
1048 test!(@binary Div, div[checked_div], call_for_all_types);
1049 test!(@binary Mul, mul[checked_mul], call_for_all_types);
1050 test!(@binary Rem, rem[checked_rem], call_for_all_types);
1051 test!(@binary Sub, sub[checked_sub], call_for_all_types);
1052
1053 test!(@binary BitAnd, bitand, call_for_unsigned_types, call_for_signed_types);
1054 test!(@binary BitOr, bitor, call_for_unsigned_types, call_for_signed_types);
1055 test!(@binary BitXor, bitxor, call_for_unsigned_types, call_for_signed_types);
1056 test!(@binary Shl, shl[checked_shl], call_for_unsigned_types, call_for_signed_types);
1057 test!(@binary Shr, shr[checked_shr], call_for_unsigned_types, call_for_signed_types);
1058
1059 test!(@unary Not, not, call_for_signed_types, call_for_unsigned_types);
1060 test!(@unary Neg, neg, call_for_signed_types, call_for_float_types);
1061 }
1062
1063 #[test]
1064 fn test_debug_impl() {
1065 let val = U16::<LE>::new(10);
1067 assert_eq!(format!("{:?}", val), "U16(10)");
1068 assert_eq!(format!("{:03?}", val), "U16(010)");
1069 assert_eq!(format!("{:x?}", val), "U16(a)");
1070 }
1071}