Skip to main content

solana_zero_copy/
unaligned.rs

1//! Unaligned primitive wrapper types for zero-copy data structures.
2//! These wrappers preserve a stable byte layout for primitive values without
3//! introducing alignment requirements from the native integer types.
4
5#[cfg(feature = "bytemuck")]
6use bytemuck_derive::{Pod, Zeroable};
7use core::cmp::PartialEq;
8#[cfg(feature = "serde")]
9use serde_derive::{Deserialize, Serialize};
10#[cfg(feature = "wincode")]
11use wincode::{SchemaRead, SchemaWrite};
12#[cfg(feature = "borsh")]
13use {
14    alloc::string::ToString,
15    borsh::{BorshDeserialize, BorshSchema, BorshSerialize},
16};
17
18/// The standard `bool` is not naturally zero-copy, define an unaligned replacement.
19///
20/// Any nonzero value is interpreted as `true`; only `0` is interpreted as `false`.
21#[cfg_attr(feature = "wincode", derive(SchemaRead, SchemaWrite))]
22#[cfg_attr(feature = "wincode", wincode(assert_zero_copy))]
23#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
24#[cfg_attr(feature = "serde", serde(from = "bool", into = "bool"))]
25#[cfg_attr(feature = "bytemuck", derive(Pod, Zeroable))]
26#[derive(Clone, Copy, Debug, Default, PartialEq)]
27#[repr(transparent)]
28pub struct Bool(pub u8);
29impl Bool {
30    pub const fn from_bool(b: bool) -> Self {
31        Self(if b { 1 } else { 0 })
32    }
33}
34
35impl From<bool> for Bool {
36    fn from(b: bool) -> Self {
37        Self::from_bool(b)
38    }
39}
40
41impl From<&bool> for Bool {
42    fn from(b: &bool) -> Self {
43        Self(if *b { 1 } else { 0 })
44    }
45}
46
47impl From<&Bool> for bool {
48    fn from(b: &Bool) -> Self {
49        b.0 != 0
50    }
51}
52
53impl From<Bool> for bool {
54    fn from(b: Bool) -> Self {
55        b.0 != 0
56    }
57}
58
59/// Simple macro for implementing conversion functions between unaligned
60/// integers and standard integers.
61///
62/// When using standard integer types in a struct, it might be required
63/// to add padding to match their alignment requirements. Unaligned types
64/// avoid this since their alignment requirement is `1`.
65#[macro_export]
66macro_rules! impl_int_conversion {
67    ($P:ty, $I:ty) => {
68        const _: () = assert!(core::mem::align_of::<$P>() == 1);
69        const _: () = assert!(core::mem::size_of::<$P>() == core::mem::size_of::<$I>());
70
71        impl $P {
72            #[inline(always)]
73            pub const fn from_primitive(n: $I) -> Self {
74                Self(n.to_le_bytes())
75            }
76
77            #[inline(always)]
78            pub fn checked_add(self, rhs: impl Into<$I>) -> Option<Self> {
79                let s: $I = self.into();
80                let other: $I = rhs.into();
81                s.checked_add(other).map(Self::from)
82            }
83
84            #[inline(always)]
85            pub fn checked_div(self, rhs: impl Into<$I>) -> Option<Self> {
86                let s: $I = self.into();
87                let other: $I = rhs.into();
88                s.checked_div(other).map(Self::from)
89            }
90
91            #[inline(always)]
92            pub fn checked_mul(self, rhs: impl Into<$I>) -> Option<Self> {
93                let s: $I = self.into();
94                let other: $I = rhs.into();
95                s.checked_mul(other).map(Self::from)
96            }
97
98            #[inline(always)]
99            pub fn checked_rem(self, rhs: impl Into<$I>) -> Option<Self> {
100                let s: $I = self.into();
101                let other: $I = rhs.into();
102                s.checked_rem(other).map(Self::from)
103            }
104
105            #[inline(always)]
106            pub fn checked_sub(self, rhs: impl Into<$I>) -> Option<Self> {
107                let s: $I = self.into();
108                let other: $I = rhs.into();
109                s.checked_sub(other).map(Self::from)
110            }
111
112            #[inline(always)]
113            pub fn saturating_add(self, rhs: impl Into<$I>) -> Self {
114                let s: $I = self.into();
115                let other: $I = rhs.into();
116                Self::from(s.saturating_add(other))
117            }
118
119            #[inline(always)]
120            pub fn saturating_div(self, rhs: impl Into<$I>) -> Self {
121                let s: $I = self.into();
122                let other: $I = rhs.into();
123                #[allow(
124                    clippy::arithmetic_side_effects,
125                    reason = "saturating_div follows primitive integer behavior and panics on division by zero"
126                )]
127                Self::from(s.saturating_div(other))
128            }
129
130            #[inline(always)]
131            pub fn saturating_mul(self, rhs: impl Into<$I>) -> Self {
132                let s: $I = self.into();
133                let other: $I = rhs.into();
134                Self::from(s.saturating_mul(other))
135            }
136
137            #[inline(always)]
138            pub fn saturating_sub(self, rhs: impl Into<$I>) -> Self {
139                let s: $I = self.into();
140                let other: $I = rhs.into();
141                Self::from(s.saturating_sub(other))
142            }
143        }
144        impl From<$I> for $P {
145            fn from(n: $I) -> Self {
146                Self::from_primitive(n)
147            }
148        }
149        impl From<$P> for $I {
150            fn from(unaligned: $P) -> Self {
151                Self::from_le_bytes(unaligned.0)
152            }
153        }
154        impl core::ops::Add<$I> for $P {
155            type Output = Self;
156
157            #[inline(always)]
158            fn add(self, rhs: $I) -> Self {
159                let s: $I = self.into();
160                #[allow(
161                    clippy::arithmetic_side_effects,
162                    reason = "add follows primitive integer behavior and wraps on overflow"
163                )]
164                Self::from(s + rhs)
165            }
166        }
167         impl core::ops::Add<$P> for $P {
168            type Output = Self;
169
170            #[inline(always)]
171            fn add(self, rhs: $P) -> Self {
172                let s: $I = self.into();
173                let other: $I = rhs.into();
174                #[allow(
175                    clippy::arithmetic_side_effects,
176                    reason = "add follows primitive integer behavior and wraps on overflow"
177                )]
178                Self::from(s + other)
179            }
180        }
181        impl core::ops::Div<$I> for $P {
182            type Output = Self;
183
184            #[inline(always)]
185            fn div(self, rhs: $I) -> Self {
186                let s: $I = self.into();
187                #[allow(
188                    clippy::arithmetic_side_effects,
189                    reason = "div follows primitive integer behavior and panics on division by zero"
190                )]
191                Self::from(s / rhs)
192            }
193        }
194        impl core::ops::Div<$P> for $P {
195            type Output = Self;
196
197            #[inline(always)]
198            fn div(self, rhs: $P) -> Self {
199                let s: $I = self.into();
200                let other: $I = rhs.into();
201                #[allow(
202                    clippy::arithmetic_side_effects,
203                    reason = "div follows primitive integer behavior and panics on division by zero"
204                )]
205                Self::from(s / other)
206            }
207        }
208        impl core::ops::Mul<$I> for $P {
209            type Output = Self;
210
211            #[inline(always)]
212            fn mul(self, rhs: $I) -> Self {
213                let s: $I = self.into();
214                #[allow(
215                    clippy::arithmetic_side_effects,
216                    reason = "mul follows primitive integer behavior and wraps on overflow"
217                )]
218                Self::from(s * rhs)
219            }
220        }
221        impl core::ops::Mul<$P> for $P {
222            type Output = Self;
223
224            #[inline(always)]
225            fn mul(self, rhs: $P) -> Self {
226                let s: $I = self.into();
227                let other: $I = rhs.into();
228                #[allow(
229                    clippy::arithmetic_side_effects,
230                    reason = "mul follows primitive integer behavior and wraps on overflow"
231                )]
232                Self::from(s * other)
233            }
234        }
235        impl core::ops::Rem<$I> for $P {
236            type Output = Self;
237
238            #[inline(always)]
239            fn rem(self, rhs: $I) -> Self {
240                let s: $I = self.into();
241                #[allow(
242                    clippy::arithmetic_side_effects,
243                    reason = "rem follows primitive integer behavior and panics on division by zero"
244                )]
245                Self::from(s % rhs)
246            }
247        }
248        impl core::ops::Rem<$P> for $P {
249            type Output = Self;
250
251            #[inline(always)]
252            fn rem(self, rhs: $P) -> Self {
253                let s: $I = self.into();
254                let other: $I = rhs.into();
255                #[allow(
256                    clippy::arithmetic_side_effects,
257                    reason = "rem follows primitive integer behavior and panics on division by zero"
258                )]
259                Self::from(s % other)
260            }
261        }
262        impl core::ops::Sub<$I> for $P {
263            type Output = Self;
264
265            #[inline(always)]
266            fn sub(self, rhs: $I) -> Self {
267                let s: $I = self.into();
268                #[allow(
269                    clippy::arithmetic_side_effects,
270                    reason = "sub follows primitive integer behavior and wraps on overflow"
271                )]
272                Self::from(s - rhs)
273            }
274        }
275        impl core::ops::Sub<$P> for $P {
276            type Output = Self;
277
278            #[inline(always)]
279            fn sub(self, rhs: $P) -> Self {
280                let s: $I = self.into();
281                let other: $I = rhs.into();
282                #[allow(
283                    clippy::arithmetic_side_effects,
284                    reason = "sub follows primitive integer behavior and wraps on overflow"
285                )]
286                Self::from(s - other)
287            }
288        }
289        impl core::ops::AddAssign<$I> for $P {
290            #[allow(
291                clippy::arithmetic_side_effects,
292                reason = "add_assign follows primitive integer behavior and wraps on overflow"
293            )]
294            #[inline(always)]
295            fn add_assign(&mut self, rhs: $I) {
296                *self = *self + rhs;
297            }
298        }
299        impl core::ops::AddAssign<$P> for $P {
300            #[allow(
301                clippy::arithmetic_side_effects,
302                reason = "add_assign follows primitive integer behavior and wraps on overflow"
303            )]
304            #[inline(always)]
305            fn add_assign(&mut self, rhs: $P) {
306                *self = *self + rhs;
307            }
308        }
309        impl core::ops::DivAssign<$I> for $P {
310            #[allow(
311                clippy::arithmetic_side_effects,
312                reason = "div_assign follows primitive integer behavior and panics on division by zero"
313            )]
314            #[inline(always)]
315            fn div_assign(&mut self, rhs: $I) {
316                *self = *self / rhs;
317            }
318        }
319        impl core::ops::DivAssign<$P> for $P {
320            #[allow(
321                clippy::arithmetic_side_effects,
322                reason = "div_assign follows primitive integer behavior and panics on division by zero"
323            )]
324            #[inline(always)]
325            fn div_assign(&mut self, rhs: $P) {
326                *self = *self / rhs;
327            }
328        }
329        impl core::ops::MulAssign<$I> for $P {
330            #[allow(
331                clippy::arithmetic_side_effects,
332                reason = "mul_assign follows primitive integer behavior and wraps on overflow"
333            )]
334            #[inline(always)]
335            fn mul_assign(&mut self, rhs: $I) {
336                *self = *self * rhs;
337            }
338        }
339        impl core::ops::MulAssign<$P> for $P {
340            #[allow(
341                clippy::arithmetic_side_effects,
342                reason = "mul_assign follows primitive integer behavior and wraps on overflow"
343            )]
344            #[inline(always)]
345            fn mul_assign(&mut self, rhs: $P) {
346                *self = *self * rhs;
347            }
348        }
349        impl core::ops::RemAssign<$I> for $P {
350            #[allow(
351                clippy::arithmetic_side_effects,
352                reason = "rem_assign follows primitive integer behavior and panics on division by zero"
353            )]
354            #[inline(always)]
355            fn rem_assign(&mut self, rhs: $I) {
356                *self = *self % rhs;
357            }
358        }
359        impl core::ops::RemAssign<$P> for $P {
360            #[allow(
361                clippy::arithmetic_side_effects,
362                reason = "rem_assign follows primitive integer behavior and panics on division by zero"
363            )]
364            #[inline(always)]
365            fn rem_assign(&mut self, rhs: $P) {
366                *self = *self % rhs;
367            }
368        }
369        impl core::ops::SubAssign<$I> for $P {
370            #[allow(
371                clippy::arithmetic_side_effects,
372                reason = "sub_assign follows primitive integer behavior and wraps on overflow"
373            )]
374            #[inline(always)]
375            fn sub_assign(&mut self, rhs: $I) {
376                *self = *self - rhs;
377            }
378        }
379        impl core::ops::SubAssign<$P> for $P {
380            #[allow(
381                clippy::arithmetic_side_effects,
382                reason = "sub_assign follows primitive integer behavior and wraps on overflow"
383            )]
384            #[inline(always)]
385            fn sub_assign(&mut self, rhs: $P) {
386                *self = *self - rhs;
387            }
388        }
389        impl core::cmp::PartialOrd<$P> for $P {
390            #[inline(always)]
391            fn partial_cmp(&self, other: &$P) -> Option<core::cmp::Ordering> {
392                let s: $I = (*self).into();
393                let o: $I = (*other).into();
394                s.partial_cmp(&o)
395            }
396        }
397    };
398}
399
400/// Unaligned `u16` type that can be embedded in bytemuck `Pod` types.
401#[cfg_attr(feature = "wincode", derive(SchemaRead, SchemaWrite))]
402#[cfg_attr(feature = "wincode", wincode(assert_zero_copy))]
403#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
404#[cfg_attr(feature = "serde", serde(from = "u16", into = "u16"))]
405#[cfg_attr(feature = "bytemuck", derive(Pod, Zeroable))]
406#[derive(Clone, Copy, Debug, Default, PartialEq)]
407#[repr(transparent)]
408pub struct U16(pub [u8; 2]);
409impl_int_conversion!(U16, u16);
410
411/// Unaligned `i16` type that can be embedded in bytemuck `Pod` types.
412#[cfg_attr(feature = "wincode", derive(SchemaRead, SchemaWrite))]
413#[cfg_attr(feature = "wincode", wincode(assert_zero_copy))]
414#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
415#[cfg_attr(feature = "serde", serde(from = "i16", into = "i16"))]
416#[cfg_attr(feature = "bytemuck", derive(Pod, Zeroable))]
417#[derive(Clone, Copy, Debug, Default, PartialEq)]
418#[repr(transparent)]
419pub struct I16(pub [u8; 2]);
420impl_int_conversion!(I16, i16);
421
422/// Unaligned `u32` type that can be embedded in bytemuck `Pod` types.
423#[cfg_attr(feature = "wincode", derive(SchemaRead, SchemaWrite))]
424#[cfg_attr(feature = "wincode", wincode(assert_zero_copy))]
425#[cfg_attr(
426    feature = "borsh",
427    derive(BorshDeserialize, BorshSerialize, BorshSchema)
428)]
429#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
430#[cfg_attr(feature = "serde", serde(from = "u32", into = "u32"))]
431#[cfg_attr(feature = "bytemuck", derive(Pod, Zeroable))]
432#[derive(Clone, Copy, Debug, Default, PartialEq)]
433#[repr(transparent)]
434pub struct U32(pub [u8; 4]);
435impl_int_conversion!(U32, u32);
436
437/// Unaligned `u64` type that can be embedded in bytemuck `Pod` types.
438#[cfg_attr(feature = "wincode", derive(SchemaRead, SchemaWrite))]
439#[cfg_attr(feature = "wincode", wincode(assert_zero_copy))]
440#[cfg_attr(
441    feature = "borsh",
442    derive(BorshDeserialize, BorshSerialize, BorshSchema)
443)]
444#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
445#[cfg_attr(feature = "serde", serde(from = "u64", into = "u64"))]
446#[cfg_attr(feature = "bytemuck", derive(Pod, Zeroable))]
447#[derive(Clone, Copy, Debug, Default, PartialEq)]
448#[repr(transparent)]
449pub struct U64(pub [u8; 8]);
450impl_int_conversion!(U64, u64);
451
452/// Unaligned `i64` type that can be embedded in bytemuck `Pod` types.
453#[cfg_attr(feature = "wincode", derive(SchemaRead, SchemaWrite))]
454#[cfg_attr(feature = "wincode", wincode(assert_zero_copy))]
455#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
456#[cfg_attr(feature = "serde", serde(from = "i64", into = "i64"))]
457#[cfg_attr(feature = "bytemuck", derive(Pod, Zeroable))]
458#[derive(Clone, Copy, Debug, Default, PartialEq)]
459#[repr(transparent)]
460pub struct I64([u8; 8]);
461impl_int_conversion!(I64, i64);
462
463/// Unaligned `u128` type that can be embedded in bytemuck `Pod` types.
464#[cfg(not(target_arch = "bpf"))]
465#[cfg_attr(feature = "wincode", derive(SchemaRead, SchemaWrite))]
466#[cfg_attr(feature = "wincode", wincode(assert_zero_copy))]
467#[cfg_attr(
468    feature = "borsh",
469    derive(BorshDeserialize, BorshSerialize, BorshSchema)
470)]
471#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
472#[cfg_attr(feature = "serde", serde(from = "u128", into = "u128"))]
473#[cfg_attr(feature = "bytemuck", derive(Pod, Zeroable))]
474#[derive(Clone, Copy, Debug, Default, PartialEq)]
475#[repr(transparent)]
476pub struct U128(pub [u8; 16]);
477#[cfg(not(target_arch = "bpf"))]
478impl_int_conversion!(U128, u128);
479
480/// Implements the `TryFrom<usize>` and `From<T> for usize` conversions for an
481/// unaligned integer type.
482macro_rules! impl_usize_conversion {
483    ($UnalignedType:ty, $PrimitiveType:ty) => {
484        impl TryFrom<usize> for $UnalignedType {
485            type Error = core::num::TryFromIntError;
486
487            fn try_from(val: usize) -> Result<Self, Self::Error> {
488                let primitive_val = <$PrimitiveType>::try_from(val)?;
489                Ok(primitive_val.into())
490            }
491        }
492
493        impl From<$UnalignedType> for usize {
494            fn from(unaligned_val: $UnalignedType) -> Self {
495                let primitive_val = <$PrimitiveType>::from(unaligned_val);
496                Self::try_from(primitive_val)
497                    .expect("value out of range for usize on this platform")
498            }
499        }
500    };
501}
502
503impl_usize_conversion!(U16, u16);
504impl_usize_conversion!(U32, u32);
505impl_usize_conversion!(U64, u64);
506#[cfg(not(target_arch = "bpf"))]
507impl_usize_conversion!(U128, u128);
508
509#[cfg(test)]
510mod tests {
511    use super::*;
512
513    #[cfg(feature = "bytemuck")]
514    #[test]
515    fn test_bool() {
516        assert!(bytemuck::try_from_bytes::<Bool>(&[]).is_err());
517        assert!(bytemuck::try_from_bytes::<Bool>(&[0, 0]).is_err());
518
519        for i in 0..=u8::MAX {
520            assert_eq!(i != 0, bool::from(*bytemuck::from_bytes::<Bool>(&[i])));
521        }
522    }
523
524    #[cfg(feature = "serde")]
525    #[test]
526    fn test_bool_serde() {
527        let unaligned_false: Bool = false.into();
528        let unaligned_true: Bool = true.into();
529
530        let serialized_false = serde_json::to_string(&unaligned_false).unwrap();
531        let serialized_true = serde_json::to_string(&unaligned_true).unwrap();
532        assert_eq!(&serialized_false, "false");
533        assert_eq!(&serialized_true, "true");
534
535        let deserialized_false = serde_json::from_str::<Bool>(&serialized_false).unwrap();
536        let deserialized_true = serde_json::from_str::<Bool>(&serialized_true).unwrap();
537        assert_eq!(unaligned_false, deserialized_false);
538        assert_eq!(unaligned_true, deserialized_true);
539    }
540
541    #[cfg(feature = "bytemuck")]
542    #[test]
543    fn test_u16() {
544        assert!(bytemuck::try_from_bytes::<U16>(&[]).is_err());
545        assert_eq!(1u16, u16::from(*bytemuck::from_bytes::<U16>(&[1, 0])));
546    }
547
548    #[cfg(feature = "serde")]
549    #[test]
550    fn test_u16_serde() {
551        let unaligned_u16: U16 = u16::MAX.into();
552
553        let serialized = serde_json::to_string(&unaligned_u16).unwrap();
554        assert_eq!(&serialized, "65535");
555
556        let deserialized = serde_json::from_str::<U16>(&serialized).unwrap();
557        assert_eq!(unaligned_u16, deserialized);
558    }
559
560    #[cfg(feature = "bytemuck")]
561    #[test]
562    fn test_i16() {
563        assert!(bytemuck::try_from_bytes::<I16>(&[]).is_err());
564        assert_eq!(-1i16, i16::from(*bytemuck::from_bytes::<I16>(&[255, 255])));
565    }
566
567    #[cfg(feature = "serde")]
568    #[test]
569    fn test_i16_serde() {
570        let unaligned_i16: I16 = i16::MAX.into();
571        let serialized = serde_json::to_string(&unaligned_i16).unwrap();
572        assert_eq!(&serialized, "32767");
573
574        let deserialized = serde_json::from_str::<I16>(&serialized).unwrap();
575        assert_eq!(unaligned_i16, deserialized);
576    }
577
578    #[cfg(feature = "bytemuck")]
579    #[test]
580    fn test_u64() {
581        assert!(bytemuck::try_from_bytes::<U64>(&[]).is_err());
582        assert_eq!(
583            1u64,
584            u64::from(*bytemuck::from_bytes::<U64>(&[1, 0, 0, 0, 0, 0, 0, 0]))
585        );
586    }
587
588    #[cfg(feature = "serde")]
589    #[test]
590    fn test_u64_serde() {
591        let unaligned_u64: U64 = u64::MAX.into();
592
593        let serialized = serde_json::to_string(&unaligned_u64).unwrap();
594        assert_eq!(&serialized, "18446744073709551615");
595
596        let deserialized = serde_json::from_str::<U64>(&serialized).unwrap();
597        assert_eq!(unaligned_u64, deserialized);
598    }
599
600    #[cfg(feature = "bytemuck")]
601    #[test]
602    fn test_i64() {
603        assert!(bytemuck::try_from_bytes::<I64>(&[]).is_err());
604        assert_eq!(
605            -1i64,
606            i64::from(*bytemuck::from_bytes::<I64>(&[
607                255, 255, 255, 255, 255, 255, 255, 255
608            ]))
609        );
610    }
611
612    #[cfg(feature = "serde")]
613    #[test]
614    fn test_i64_serde() {
615        let unaligned_i64: I64 = i64::MAX.into();
616
617        let serialized = serde_json::to_string(&unaligned_i64).unwrap();
618        assert_eq!(&serialized, "9223372036854775807");
619
620        let deserialized = serde_json::from_str::<I64>(&serialized).unwrap();
621        assert_eq!(unaligned_i64, deserialized);
622    }
623
624    #[cfg(not(target_arch = "bpf"))]
625    #[cfg(feature = "bytemuck")]
626    #[test]
627    fn test_u128() {
628        assert!(bytemuck::try_from_bytes::<U128>(&[]).is_err());
629        assert_eq!(
630            1u128,
631            u128::from(*bytemuck::from_bytes::<U128>(&[
632                1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
633            ]))
634        );
635    }
636
637    #[cfg(not(target_arch = "bpf"))]
638    #[cfg(feature = "serde")]
639    #[test]
640    fn test_u128_serde() {
641        let unaligned_u128: U128 = u128::MAX.into();
642
643        let serialized = serde_json::to_string(&unaligned_u128).unwrap();
644        assert_eq!(&serialized, "340282366920938463463374607431768211455");
645
646        let deserialized = serde_json::from_str::<U128>(&serialized).unwrap();
647        assert_eq!(unaligned_u128, deserialized);
648    }
649
650    macro_rules! test_usize_roundtrip {
651        ($test_name:ident, $UnalignedType:ty, $max:expr) => {
652            #[test]
653            fn $test_name() {
654                // zero
655                let unaligned = <$UnalignedType>::try_from(0usize).unwrap();
656                assert_eq!(usize::from(unaligned), 0);
657
658                // mid-range
659                let unaligned = <$UnalignedType>::try_from(42usize).unwrap();
660                assert_eq!(usize::from(unaligned), 42);
661
662                // max
663                let max = $max as usize;
664                let unaligned = <$UnalignedType>::try_from(max).unwrap();
665                assert_eq!(usize::from(unaligned), max);
666            }
667        };
668    }
669
670    test_usize_roundtrip!(test_usize_roundtrip_u16, U16, u16::MAX);
671    test_usize_roundtrip!(test_usize_roundtrip_u32, U32, u32::MAX);
672    test_usize_roundtrip!(test_usize_roundtrip_u64, U64, u64::MAX);
673    #[cfg(not(target_arch = "bpf"))]
674    test_usize_roundtrip!(test_usize_roundtrip_u128, U128, u128::MAX);
675
676    #[cfg(feature = "wincode")]
677    mod wincode_tests {
678        use {super::*, test_case::test_case};
679
680        #[test_case(Bool::from_bool(true))]
681        #[test_case(Bool::from_bool(false))]
682        #[test_case(U16::from_primitive(u16::MAX))]
683        #[test_case(I16::from_primitive(i16::MIN))]
684        #[test_case(U32::from_primitive(u32::MAX))]
685        #[test_case(U64::from_primitive(u64::MAX))]
686        #[test_case(I64::from_primitive(i64::MIN))]
687        #[cfg(not(target_arch = "bpf"))]
688        #[test_case(U128::from_primitive(u128::MAX))]
689        fn wincode_roundtrip<
690            T: PartialEq
691                + core::fmt::Debug
692                + wincode::ZeroCopy
693                + for<'de> wincode::SchemaRead<'de, wincode::config::DefaultConfig, Dst = T>
694                + wincode::SchemaWrite<wincode::config::DefaultConfig, Src = T>,
695        >(
696            value: T,
697        ) {
698            let size = wincode::serialized_size(&value).unwrap() as usize;
699            let mut bytes = [0u8; 32];
700            assert!(size <= bytes.len());
701            wincode::serialize_into(&mut bytes[..size], &value).unwrap();
702
703            let deserialized: T = wincode::deserialize(&bytes[..size]).unwrap();
704            assert_eq!(value, deserialized);
705
706            let zero_copy_ref = <T as wincode::ZeroCopy>::from_bytes(&bytes[..size]).unwrap();
707            assert_eq!(&value, zero_copy_ref);
708        }
709    }
710
711    #[derive(Clone, Copy, Debug)]
712    enum ArithmeticMethod {
713        CheckedAdd,
714        CheckedDiv,
715        CheckedMul,
716        CheckedRem,
717        CheckedSub,
718        SaturatingAdd,
719        SaturatingDiv,
720        SaturatingMul,
721        SaturatingSub,
722        Add,
723        Div,
724        Mul,
725        Rem,
726        Sub,
727        AddAssign,
728        DivAssign,
729        MulAssign,
730        RemAssign,
731        SubAssign,
732        PartialOrd,
733    }
734
735    macro_rules! test_arithmetic_methods {
736        ($test_name:ident, $UnalignedType:ty, $PrimitiveType:ty, $min:expr, $max:expr) => {
737            #[test_case::test_case(ArithmeticMethod::CheckedAdd ; "checked_add")]
738            #[test_case::test_case(ArithmeticMethod::CheckedDiv ; "checked_div")]
739            #[test_case::test_case(ArithmeticMethod::CheckedMul ; "checked_mul")]
740            #[test_case::test_case(ArithmeticMethod::CheckedRem ; "checked_rem")]
741            #[test_case::test_case(ArithmeticMethod::CheckedSub ; "checked_sub")]
742            #[test_case::test_case(ArithmeticMethod::SaturatingAdd ; "saturating_add")]
743            #[test_case::test_case(ArithmeticMethod::SaturatingDiv ; "saturating_div")]
744            #[test_case::test_case(ArithmeticMethod::SaturatingMul ; "saturating_mul")]
745            #[test_case::test_case(ArithmeticMethod::SaturatingSub ; "saturating_sub")]
746            #[test_case::test_case(ArithmeticMethod::Add ; "add")]
747            #[test_case::test_case(ArithmeticMethod::Div ; "div")]
748            #[test_case::test_case(ArithmeticMethod::Mul ; "mul")]
749            #[test_case::test_case(ArithmeticMethod::Rem ; "rem")]
750            #[test_case::test_case(ArithmeticMethod::Sub ; "sub")]
751            #[test_case::test_case(ArithmeticMethod::AddAssign ; "add_assign")]
752            #[test_case::test_case(ArithmeticMethod::DivAssign ; "div_assign")]
753            #[test_case::test_case(ArithmeticMethod::MulAssign ; "mul_assign")]
754            #[test_case::test_case(ArithmeticMethod::RemAssign ; "rem_assign")]
755            #[test_case::test_case(ArithmeticMethod::SubAssign ; "sub_assign")]
756            #[test_case::test_case(ArithmeticMethod::PartialOrd ; "partial_ord")]
757            #[allow(clippy::arithmetic_side_effects)]
758            fn $test_name(method: ArithmeticMethod) {
759                let min = <$UnalignedType>::from_primitive($min);
760                let max = <$UnalignedType>::from_primitive($max);
761                let zero = 0 as $PrimitiveType;
762                let one = 1 as $PrimitiveType;
763                let two = 2 as $PrimitiveType;
764                let twenty_one = 21 as $PrimitiveType;
765                let forty = 40 as $PrimitiveType;
766                let forty_one = 41 as $PrimitiveType;
767                let forty_two = 42 as $PrimitiveType;
768                let forty_three = 43 as $PrimitiveType;
769                let forty_four = 44 as $PrimitiveType;
770                let eighty_four = 84 as $PrimitiveType;
771
772                match method {
773                    ArithmeticMethod::CheckedAdd => {
774                        assert_eq!(max.checked_add(one), None);
775                        assert_eq!(
776                            <$UnalignedType>::from_primitive(forty).checked_add(one),
777                            Some(<$UnalignedType>::from_primitive(forty_one))
778                        );
779                    }
780                    ArithmeticMethod::CheckedDiv => {
781                        assert_eq!(
782                            <$UnalignedType>::from_primitive(eighty_four).checked_div(two),
783                            Some(<$UnalignedType>::from_primitive(forty_two))
784                        );
785                        assert_eq!(
786                            <$UnalignedType>::from_primitive(eighty_four).checked_div(zero),
787                            None
788                        );
789                    }
790                    ArithmeticMethod::CheckedMul => {
791                        assert_eq!(max.checked_mul(two), None);
792                        assert_eq!(
793                            <$UnalignedType>::from_primitive(forty_two).checked_mul(two),
794                            Some(<$UnalignedType>::from_primitive(eighty_four))
795                        );
796                    }
797                    ArithmeticMethod::CheckedRem => {
798                        assert_eq!(
799                            <$UnalignedType>::from_primitive(forty_four).checked_rem(forty_three),
800                            Some(<$UnalignedType>::from_primitive(one))
801                        );
802                    }
803                    ArithmeticMethod::CheckedSub => {
804                        assert_eq!(min.checked_sub(one), None);
805                        assert_eq!(
806                            max.checked_sub(max),
807                            Some(<$UnalignedType>::from_primitive(zero))
808                        );
809                    }
810                    ArithmeticMethod::SaturatingAdd => {
811                        assert_eq!(max.saturating_add(one), max);
812                        assert_eq!(
813                            <$UnalignedType>::from_primitive(zero).saturating_add(one),
814                            <$UnalignedType>::from_primitive(one)
815                        );
816                    }
817                    ArithmeticMethod::SaturatingDiv => {
818                        assert_eq!(
819                            <$UnalignedType>::from_primitive(eighty_four).saturating_div(two),
820                            <$UnalignedType>::from_primitive(forty_two)
821                        );
822                    }
823                    ArithmeticMethod::SaturatingMul => {
824                        assert_eq!(max.saturating_mul(two), max);
825                    }
826                    ArithmeticMethod::SaturatingSub => {
827                        assert_eq!(min.saturating_sub(one), min);
828                    }
829                    ArithmeticMethod::Add => {
830                        assert_eq!(
831                            <$UnalignedType>::from_primitive(forty) + two,
832                            <$UnalignedType>::from_primitive(forty_two)
833                        );
834                    }
835                    ArithmeticMethod::Div => {
836                        assert_eq!(
837                            <$UnalignedType>::from_primitive(eighty_four) / two,
838                            <$UnalignedType>::from_primitive(forty_two)
839                        );
840                    }
841                    ArithmeticMethod::Mul => {
842                        assert_eq!(
843                            <$UnalignedType>::from_primitive(twenty_one) * two,
844                            <$UnalignedType>::from_primitive(forty_two)
845                        );
846                    }
847                    ArithmeticMethod::Rem => {
848                        assert_eq!(
849                            <$UnalignedType>::from_primitive(forty_four) % forty_three,
850                            <$UnalignedType>::from_primitive(one)
851                        );
852                    }
853                    ArithmeticMethod::Sub => {
854                        assert_eq!(
855                            <$UnalignedType>::from_primitive(forty_four) - two,
856                            <$UnalignedType>::from_primitive(forty_two)
857                        );
858                    }
859                    ArithmeticMethod::AddAssign => {
860                        let mut value = <$UnalignedType>::from_primitive(forty);
861                        value += two;
862                        assert_eq!(value, <$UnalignedType>::from_primitive(forty_two));
863                    }
864                    ArithmeticMethod::DivAssign => {
865                        let mut value = <$UnalignedType>::from_primitive(eighty_four);
866                        value /= two;
867                        assert_eq!(value, <$UnalignedType>::from_primitive(forty_two));
868                    }
869                    ArithmeticMethod::MulAssign => {
870                        let mut value = <$UnalignedType>::from_primitive(twenty_one);
871                        value *= two;
872                        assert_eq!(value, <$UnalignedType>::from_primitive(forty_two));
873                    }
874                    ArithmeticMethod::RemAssign => {
875                        let mut value = <$UnalignedType>::from_primitive(forty_four);
876                        value %= forty_three;
877                        assert_eq!(value, <$UnalignedType>::from_primitive(one));
878                    }
879                    ArithmeticMethod::SubAssign => {
880                        let mut value = <$UnalignedType>::from_primitive(forty_four);
881                        value -= two;
882                        assert_eq!(value, <$UnalignedType>::from_primitive(forty_two));
883                    }
884                    ArithmeticMethod::PartialOrd => {
885                        assert!(
886                            <$UnalignedType>::from_primitive(forty_one)
887                                < <$UnalignedType>::from_primitive(forty_two)
888                        );
889                        assert!(max > min);
890                    }
891                }
892            }
893        };
894    }
895
896    test_arithmetic_methods!(test_arithmetic_methods_u16, U16, u16, u16::MIN, u16::MAX);
897    test_arithmetic_methods!(test_arithmetic_methods_i16, I16, i16, i16::MIN, i16::MAX);
898    test_arithmetic_methods!(test_arithmetic_methods_u32, U32, u32, u32::MIN, u32::MAX);
899    test_arithmetic_methods!(test_arithmetic_methods_u64, U64, u64, u64::MIN, u64::MAX);
900    test_arithmetic_methods!(test_arithmetic_methods_i64, I64, i64, i64::MIN, i64::MAX);
901    #[cfg(not(target_arch = "bpf"))]
902    test_arithmetic_methods!(
903        test_arithmetic_methods_u128,
904        U128,
905        u128,
906        u128::MIN,
907        u128::MAX
908    );
909}