1#[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#[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#[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#[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#[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#[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#[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#[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#[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
480macro_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 let unaligned = <$UnalignedType>::try_from(0usize).unwrap();
656 assert_eq!(usize::from(unaligned), 0);
657
658 let unaligned = <$UnalignedType>::try_from(42usize).unwrap();
660 assert_eq!(usize::from(unaligned), 42);
661
662 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}