1#[cfg(feature = "alloc")]
2extern crate alloc;
3
4#[cfg(feature = "alloc")]
5use alloc::vec::Vec;
6use bytemuck::{NoUninit, Pod, Zeroable};
7use core::fmt;
8use core::fmt::{Debug, Display, LowerHex, Octal, UpperHex};
9use core::hash::{Hash, Hasher};
10use core::num::ParseIntError;
11use core::ops::{
12 Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Div, DivAssign,
13 Mul, MulAssign, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign, Sub, SubAssign,
14};
15use core::{
16 ops::{Neg, Not},
17 str::FromStr,
18};
19
20#[cfg(feature = "num-cast")]
21use num_traits::{FromPrimitive, Num, NumCast, One, Signed, ToBytes, ToPrimitive, Zero};
22#[cfg(not(feature = "num-cast"))]
23use num_traits::{FromPrimitive, Num, One, Signed, ToBytes, ToPrimitive, Zero};
24
25use crate::repr::I24Repr;
26use crate::{TryFromIntError, i24, out_of_range};
27
28#[allow(non_camel_case_types)]
29#[repr(transparent)]
30#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
31pub struct I24(I24Repr);
99
100unsafe impl Zeroable for I24 where I24Repr: Zeroable {}
102
103unsafe impl NoUninit for I24 where I24Repr: NoUninit {}
106
107impl FromPrimitive for I24
108where
109 I24Repr: FromPrimitive,
110{
111 #[inline]
112 fn from_i64(n: i64) -> Option<Self> {
113 I24Repr::from_i64(n).map(Self)
114 }
115
116 #[inline]
117 fn from_u64(n: u64) -> Option<Self> {
118 I24Repr::from_u64(n).map(Self)
119 }
120}
121
122impl I24 {
123 pub const BITS: u32 = 24;
125
126 pub const MIN: I24 = i24!(I24Repr::MIN);
128
129 pub const MAX: I24 = i24!(I24Repr::MAX);
131
132 pub const ZERO: I24 = i24!(I24Repr::ZERO);
134
135 #[inline]
140 pub const fn as_bits(&self) -> &u32 {
141 self.0.as_bits()
142 }
143
144 #[inline]
149 pub const fn to_bits(self) -> u32 {
150 self.0.to_bits()
151 }
152
153 #[inline]
155 const unsafe fn from_bits(bits: u32) -> I24 {
156 Self(unsafe { I24Repr::from_bits(bits) })
157 }
158
159 #[inline]
161 const fn from_bits_truncate(bits: u32) -> I24 {
162 Self(unsafe { I24Repr::from_bits(bits & I24Repr::BITS_MASK) })
164 }
165
166 #[inline]
174 pub const fn to_i32(self) -> i32 {
175 self.0.to_i32()
176 }
177
178 #[inline]
190 pub const fn wrapping_from_i32(n: i32) -> Self {
191 Self(I24Repr::wrapping_from_i32(n))
192 }
193
194 #[inline]
206 pub const fn saturating_from_i32(n: i32) -> Self {
207 Self(I24Repr::saturating_from_i32(n))
208 }
209
210 #[inline]
212 pub const fn swap_bytes(self) -> Self {
213 Self(self.0.swap_bytes())
214 }
215
216 #[inline]
219 pub const fn to_le(self) -> Self {
220 Self(self.0.to_le())
221 }
222
223 #[inline]
226 pub const fn to_be(self) -> Self {
227 Self(self.0.to_be())
228 }
229
230 #[inline]
234 pub const fn to_ne_bytes(self) -> [u8; 3] {
235 self.0.to_ne_bytes()
236 }
237
238 #[inline]
240 pub const fn to_le_bytes(self) -> [u8; 3] {
241 self.0.to_le_bytes()
242 }
243
244 #[inline]
246 pub const fn to_be_bytes(self) -> [u8; 3] {
247 self.0.to_be_bytes()
248 }
249
250 #[inline]
260 pub const fn from_ne_bytes(bytes: [u8; 3]) -> Self {
261 Self(I24Repr::from_ne_bytes(bytes))
262 }
263
264 #[inline]
274 pub const fn from_le_bytes(bytes: [u8; 3]) -> Self {
275 Self(I24Repr::from_le_bytes(bytes))
276 }
277
278 #[inline]
288 pub const fn from_be_bytes(bytes: [u8; 3]) -> Self {
289 Self(I24Repr::from_be_bytes(bytes))
290 }
291
292 pub fn checked_add(self, other: Self) -> Option<Self> {
302 self.to_i32()
303 .checked_add(other.to_i32())
304 .and_then(Self::try_from_i32)
305 }
306
307 pub fn checked_sub(self, other: Self) -> Option<Self> {
317 self.to_i32()
318 .checked_sub(other.to_i32())
319 .and_then(Self::try_from_i32)
320 }
321
322 pub fn checked_mul(self, other: Self) -> Option<Self> {
332 self.to_i32()
333 .checked_mul(other.to_i32())
334 .and_then(Self::try_from_i32)
335 }
336
337 pub fn checked_div(self, other: Self) -> Option<Self> {
347 self.to_i32()
348 .checked_div(other.to_i32())
349 .and_then(Self::try_from_i32)
350 }
351
352 pub fn checked_rem(self, other: Self) -> Option<Self> {
362 self.to_i32()
363 .checked_rem(other.to_i32())
364 .and_then(Self::try_from_i32)
365 }
366
367 pub fn checked_neg(self) -> Option<Self> {
374 self.to_i32().checked_neg().and_then(Self::try_from_i32)
375 }
376
377 #[inline]
391 pub fn abs(self) -> Self {
392 if self.to_i32() < 0 { -self } else { self }
393 }
394
395 #[inline]
410 pub fn wrapping_abs(self) -> Self {
411 if self.to_i32() < 0 { -self } else { self }
412 }
413
414 #[inline]
429 pub fn saturating_abs(self) -> Self {
430 if self == Self::MIN {
431 Self::MAX
432 } else if self.to_i32() < 0 {
433 -self
434 } else {
435 self
436 }
437 }
438
439 #[inline]
456 pub fn signum(self) -> Self {
457 let val = self.to_i32();
458 match val.cmp(&0) {
459 core::cmp::Ordering::Greater => Self::one(),
460 core::cmp::Ordering::Less => -Self::one(),
461 core::cmp::Ordering::Equal => Self::zero(),
462 }
463 }
464
465 #[inline]
476 pub const fn is_negative(self) -> bool {
477 self.to_i32() < 0
478 }
479
480 #[inline]
491 pub const fn is_positive(self) -> bool {
492 self.to_i32() > 0
493 }
494
495 #[inline]
509 pub fn abs_sub(&self, other: &Self) -> Self {
510 if *self <= *other {
511 Self::zero()
512 } else {
513 *self - *other
514 }
515 }
516
517 #[inline]
535 pub fn clamp(self, min: Self, max: Self) -> Self {
536 assert!(min <= max);
537 if self < min {
538 min
539 } else if self > max {
540 max
541 } else {
542 self
543 }
544 }
545
546 #[inline]
556 pub fn min(self, other: Self) -> Self {
557 if self <= other { self } else { other }
558 }
559
560 #[inline]
570 pub fn max(self, other: Self) -> Self {
571 if self >= other { self } else { other }
572 }
573
574 #[inline]
584 pub fn wrapping_add(self, rhs: Self) -> Self {
585 self + rhs }
587
588 #[inline]
598 pub fn wrapping_sub(self, rhs: Self) -> Self {
599 self - rhs }
601
602 #[inline]
612 pub fn wrapping_mul(self, rhs: Self) -> Self {
613 self * rhs }
615
616 #[inline]
633 pub fn wrapping_div(self, rhs: Self) -> Self {
634 self / rhs }
636
637 #[inline]
654 pub fn wrapping_rem(self, rhs: Self) -> Self {
655 self % rhs }
657
658 #[inline]
668 pub fn wrapping_neg(self) -> Self {
669 -self }
671
672 #[inline]
683 pub fn saturating_add(self, rhs: Self) -> Self {
684 self.to_i32()
685 .saturating_add(rhs.to_i32())
686 .try_into()
687 .unwrap_or_else(|_| {
688 if rhs.to_i32() > 0 {
689 Self::MAX
690 } else {
691 Self::MIN
692 }
693 })
694 }
695
696 #[inline]
707 pub fn saturating_sub(self, rhs: Self) -> Self {
708 self.to_i32()
709 .saturating_sub(rhs.to_i32())
710 .try_into()
711 .unwrap_or_else(|_| {
712 if rhs.to_i32() < 0 {
713 Self::MAX
714 } else {
715 Self::MIN
716 }
717 })
718 }
719
720 #[inline]
731 pub const fn saturating_mul(self, rhs: Self) -> Self {
732 let result = self.to_i32().saturating_mul(rhs.to_i32());
733 Self::saturating_from_i32(result)
734 }
735
736 #[inline]
753 pub fn saturating_div(self, rhs: Self) -> Self {
754 if self == Self::MIN && rhs.to_i32() == -1 {
755 Self::MAX
756 } else {
757 self / rhs
758 }
759 }
760
761 #[inline]
771 pub fn saturating_neg(self) -> Self {
772 if self == Self::MIN { Self::MAX } else { -self }
773 }
774
775 #[cfg(feature = "alloc")]
791 pub fn read_i24s_le(bytes: &[u8]) -> Option<Vec<I24>> {
792 if !bytes.len().is_multiple_of(3) {
793 return None;
794 }
795
796 let mut result = Vec::with_capacity(bytes.len() / 3);
797
798 bytes.chunks_exact(3).for_each(|chunk| {
799 result.push(I24::from_le_bytes([chunk[0], chunk[1], chunk[2]]));
800 });
801
802 Some(result)
803 }
804
805 #[cfg(feature = "alloc")]
820 pub const fn read_i24s_le_slice(bytes: &[u8]) -> Option<&[I24]> {
821 if !bytes.len().is_multiple_of(3) {
822 return None;
823 }
824
825 let result =
826 unsafe { core::slice::from_raw_parts(bytes.as_ptr() as *const I24, bytes.len() / 3) };
827
828 Some(result)
829 }
830
831 #[cfg(feature = "alloc")]
847 pub fn read_i24s_be(bytes: &[u8]) -> Option<Vec<I24>> {
848 if !bytes.len().is_multiple_of(3) {
849 return None;
850 }
851
852 let mut result = Vec::with_capacity(bytes.len() / 3);
853
854 bytes.chunks_exact(3).for_each(|chunk| {
855 result.push(I24::from_be_bytes([chunk[0], chunk[1], chunk[2]]));
856 });
857
858 Some(result)
859 }
860
861 #[cfg(feature = "alloc")]
876 pub unsafe fn read_i24s_le_unchecked(bytes: &[u8]) -> Vec<I24> {
877 debug_assert!(bytes.len().is_multiple_of(3));
878 let chunks: &[I24Bytes] = bytemuck::cast_slice(bytes);
879 chunks.iter().map(|b| b.to_i24_le()).collect()
880 }
881
882 #[cfg(feature = "alloc")]
883 pub unsafe fn read_i24s_le_slice_unchecked(bytes: &[u8]) -> &[I24] {
889 debug_assert!(bytes.len().is_multiple_of(3));
890 unsafe { core::slice::from_raw_parts(bytes.as_ptr() as *const I24, bytes.len() / 3) }
891 }
892
893 #[cfg(feature = "alloc")]
908 pub unsafe fn read_i24s_be_unchecked(bytes: &[u8]) -> Vec<I24> {
909 debug_assert!(bytes.len().is_multiple_of(3));
910 let chunks: &[I24Bytes] = bytemuck::cast_slice(bytes);
911 chunks.iter().map(|b| b.to_i24_be()).collect()
912 }
913
914 #[cfg(feature = "alloc")]
916 pub fn write_i24s_le(values: &[I24]) -> Vec<u8> {
917 let mut bytes = Vec::with_capacity(values.len() * 3);
918 for &value in values {
919 bytes.extend_from_slice(&value.to_le_bytes());
920 }
921 bytes
922 }
923
924 #[cfg(feature = "alloc")]
926 pub fn write_i24s_be(values: &[I24]) -> Vec<u8> {
927 let mut bytes = Vec::with_capacity(values.len() * 3);
928 for &value in values {
929 bytes.extend_from_slice(&value.to_be_bytes());
930 }
931 bytes
932 }
933
934 #[cfg(feature = "alloc")]
945 pub fn read_i24s_ne(bytes: &[u8]) -> Option<Vec<I24>> {
946 if !bytes.len().is_multiple_of(3) {
947 return None;
948 }
949
950 let mut result = Vec::with_capacity(bytes.len() / 3);
951
952 bytes.chunks_exact(3).for_each(|chunk| {
953 result.push(I24::from_ne_bytes([chunk[0], chunk[1], chunk[2]]));
954 });
955
956 Some(result)
957 }
958
959 #[cfg(feature = "alloc")]
961 pub fn write_i24s_ne(values: &[I24]) -> Vec<u8> {
962 let mut bytes = Vec::with_capacity(values.len() * 3);
963 for &value in values {
964 bytes.extend_from_slice(&value.to_ne_bytes());
965 }
966 bytes
967 }
968}
969
970macro_rules! impl_bin_op {
971 ($(impl $op: ident = $assign: ident $assign_fn: ident { $($impl: tt)* })+) => {$(
972 impl_bin_op!(impl $op = $assign $assign_fn for I24 { $($impl)* });
973 impl_bin_op!(impl $op = $assign $assign_fn for &I24 { $($impl)* });
974 )+};
975
976 (impl $op: ident = $assign: ident $assign_fn: ident for $ty:ty {
977 fn $meth: ident($self: tt, $other: ident) {
978 $($impl: tt)*
979 }
980 }) => {
981 impl $op<$ty> for I24 {
982 type Output = Self;
983
984 #[inline]
985 fn $meth($self, $other: $ty) -> Self {
986 $($impl)*
987 }
988 }
989
990 impl $op<$ty> for &I24 {
991 type Output = I24;
992
993 #[inline]
994 fn $meth(self, other: $ty) -> I24 {
995 <I24 as $op<$ty>>::$meth(*self, other)
996 }
997 }
998
999 impl $assign<$ty> for I24 {
1000 #[inline]
1001 fn $assign_fn(&mut self, rhs: $ty) {
1002 *self = $op::$meth(*self, rhs)
1003 }
1004 }
1005 };
1006}
1007
1008impl_bin_op! {
1009 impl Add = AddAssign add_assign {
1010 fn add(self, other) {
1011 Self::from_bits_truncate(self.to_bits().wrapping_add(other.to_bits()))
1014 }
1015 }
1016
1017 impl Sub = SubAssign sub_assign {
1018 fn sub(self, other) {
1019 Self::from_bits_truncate(self.to_bits().wrapping_sub(other.to_bits()))
1022 }
1023 }
1024
1025 impl Mul = MulAssign mul_assign {
1026 fn mul(self, other) {
1027 Self::from_bits_truncate(self.to_bits().wrapping_mul(other.to_bits()))
1030 }
1031 }
1032
1033 impl Div = DivAssign div_assign {
1034 fn div(self, other) {
1035 let other_val = unsafe { I24::from_bits(other.to_bits()) };
1036 let result = <I24>::to_i32(self).wrapping_div(<I24>::to_i32(other_val));
1037 Self::wrapping_from_i32(result)
1038 }
1039 }
1040
1041 impl Rem = RemAssign rem_assign {
1042 fn rem(self, other) {
1043 let other_val = unsafe { I24::from_bits(other.to_bits()) };
1044 let result = <I24>::to_i32(self).wrapping_rem(<I24>::to_i32(other_val));
1045 Self::wrapping_from_i32(result)
1046 }
1047 }
1048
1049
1050 impl BitAnd = BitAndAssign bitand_assign {
1051 fn bitand(self, rhs) {
1052 let bits = self.to_bits() & rhs.to_bits();
1053 unsafe { I24::from_bits(bits) }
1057 }
1058 }
1059
1060 impl BitOr = BitOrAssign bitor_assign {
1061 fn bitor(self, rhs) {
1062 let bits = self.to_bits() | rhs.to_bits();
1063 unsafe { I24::from_bits(bits) }
1067 }
1068 }
1069
1070 impl BitXor = BitXorAssign bitxor_assign {
1071 fn bitxor(self, rhs) {
1072 let bits = self.to_bits() ^ rhs.to_bits();
1073 unsafe { I24::from_bits(bits) }
1077 }
1078 }
1079}
1080
1081impl Hash for I24 {
1082 fn hash<H: Hasher>(&self, state: &mut H) {
1083 I24Repr::hash(&self.0, state)
1084 }
1085
1086 fn hash_slice<H: Hasher>(data: &[Self], state: &mut H)
1087 where
1088 Self: Sized,
1089 {
1090 I24Repr::hash_slice(
1092 unsafe { core::mem::transmute::<&[Self], &[I24Repr]>(data) },
1093 state,
1094 )
1095 }
1096}
1097
1098macro_rules! impl_from {
1099 ($($ty: ty : $func_name: ident),+ $(,)?) => {$(
1100 impl From<$ty> for I24 {
1101 fn from(value: $ty) -> Self {
1102 Self::$func_name(value)
1103 }
1104 }
1105
1106 impl I24 {
1107 #[doc = concat!("Creates an `I24` from a `", stringify!($ty), "` value.")]
1108 pub const fn $func_name(value: $ty) -> Self {
1111 Self(I24Repr::$func_name(value))
1112 }
1113 }
1114 )+};
1115}
1116
1117macro_rules! impl_try {
1118 ($($ty: ty : $func_name: ident),+ $(,)?) => {$(
1119 impl TryFrom<$ty> for I24 {
1120 type Error = TryFromIntError;
1121
1122 fn try_from(value: $ty) -> Result<Self, Self::Error> {
1123 Self::$func_name(value).ok_or_else(out_of_range)
1124 }
1125 }
1126
1127 impl I24 {
1128 #[doc = concat!("Tries to create an `I24` from a `", stringify!($ty), "` value.")]
1129 pub const fn $func_name(value: $ty) -> Option<Self> {
1133 match I24Repr::$func_name(value) {
1134 Some(x) => Some(Self(x)),
1135 None => None
1136 }
1137 }
1138 }
1139 )+};
1140}
1141
1142impl_from! {
1143 u8: from_u8,
1144 u16: from_u16,
1145 bool: from_bool,
1146
1147 i8: from_i8,
1148 i16: from_i16,
1149}
1150
1151impl_try! {
1152 u32 : try_from_u32,
1153 u64 : try_from_u64,
1154 u128: try_from_u128,
1155
1156 i32 : try_from_i32,
1157 i64 : try_from_i64,
1158 i128: try_from_i128,
1159}
1160
1161impl From<I24> for i32 {
1163 #[inline]
1164 fn from(value: I24) -> Self {
1165 value.to_i32()
1166 }
1167}
1168
1169impl From<I24> for i64 {
1170 #[inline]
1171 fn from(value: I24) -> Self {
1172 value.to_i32() as i64
1173 }
1174}
1175
1176impl From<I24> for i128 {
1177 #[inline]
1178 fn from(value: I24) -> Self {
1179 value.to_i32() as i128
1180 }
1181}
1182
1183impl From<I24> for isize {
1184 #[inline]
1185 fn from(value: I24) -> Self {
1186 value.to_i32() as isize
1187 }
1188}
1189
1190impl One for I24 {
1191 fn one() -> Self {
1192 i24!(1)
1193 }
1194}
1195
1196impl Zero for I24 {
1197 #[inline]
1198 fn zero() -> Self {
1199 Self::zeroed()
1200 }
1201
1202 #[inline]
1203 fn is_zero(&self) -> bool {
1204 Self::zeroed() == *self
1205 }
1206}
1207
1208pub const fn from_str_error(bad_val: &str) -> ParseIntError {
1209 match i8::from_str_radix(bad_val, 10) {
1210 Err(err) => err,
1211 Ok(_) => unreachable!(),
1212 }
1213}
1214
1215const POSITIVE_OVERFLOW: ParseIntError = from_str_error("9999999999999999999999999999999999999999");
1216
1217const NEGATIVE_OVERFLOW: ParseIntError =
1218 from_str_error("-9999999999999999999999999999999999999999");
1219
1220macro_rules! from_str {
1221 ($meth: ident($($args: tt)*)) => {
1222 i32::$meth($($args)*)
1223 .and_then(|x| I24::try_from_i32(x).ok_or_else(|| {
1224 if x < 0 {
1225 NEGATIVE_OVERFLOW
1226 } else {
1227 POSITIVE_OVERFLOW
1228 }
1229 }))
1230 };
1231}
1232
1233impl Num for I24 {
1234 type FromStrRadixErr = ParseIntError;
1235 fn from_str_radix(str: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr> {
1236 from_str!(from_str_radix(str, radix))
1237 }
1238}
1239
1240impl FromStr for I24 {
1241 type Err = ParseIntError;
1242
1243 fn from_str(str: &str) -> Result<Self, Self::Err> {
1244 from_str!(from_str(str))
1245 }
1246}
1247
1248impl Neg for I24 {
1249 type Output = Self;
1250
1251 #[inline]
1252 fn neg(self) -> Self {
1253 I24::from_bits_truncate((!self.to_bits()) + 1)
1255 }
1256}
1257
1258impl Signed for I24 {
1259 #[inline]
1260 fn abs(&self) -> Self {
1261 (*self).abs()
1262 }
1263
1264 #[inline]
1265 fn abs_sub(&self, other: &Self) -> Self {
1266 (*self).abs_sub(other)
1267 }
1268
1269 #[inline]
1270 fn signum(&self) -> Self {
1271 (*self).signum()
1272 }
1273
1274 #[inline]
1275 fn is_positive(&self) -> bool {
1276 (*self).is_positive()
1277 }
1278
1279 #[inline]
1280 fn is_negative(&self) -> bool {
1281 (*self).is_negative()
1282 }
1283}
1284
1285impl Not for I24 {
1286 type Output = Self;
1287
1288 #[inline]
1289 fn not(self) -> Self {
1290 I24::from_bits_truncate(!self.to_bits())
1291 }
1292}
1293
1294impl Shl<u32> for I24 {
1295 type Output = Self;
1296
1297 #[inline]
1298 fn shl(self, rhs: u32) -> Self::Output {
1299 let n = rhs % 24;
1301 Self::from_bits_truncate(self.to_bits() << n)
1302 }
1303}
1304
1305impl Shr<u32> for I24 {
1306 type Output = Self;
1307
1308 #[inline]
1309 fn shr(self, rhs: u32) -> Self::Output {
1310 let n = rhs % 24;
1312
1313 unsafe { Self::from_bits(((self.to_bits() << 8) as i32 >> n) as u32 >> 8) }
1326 }
1327}
1328
1329impl ShrAssign<u32> for I24 {
1330 #[inline]
1331 fn shr_assign(&mut self, rhs: u32) {
1332 *self = Shr::shr(*self, rhs)
1333 }
1334}
1335
1336impl ShlAssign<u32> for I24 {
1337 #[inline]
1338 fn shl_assign(&mut self, rhs: u32) {
1339 *self = Shl::shl(*self, rhs)
1340 }
1341}
1342
1343macro_rules! impl_fmt {
1344 ($(impl $name: path)+) => {$(
1345 impl $name for I24 {
1346 #[inline]
1347 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1348 <i32 as $name>::fmt(&I24::to_i32(*self), f)
1349 }
1350 }
1351 )*};
1352}
1353
1354macro_rules! impl_bits_fmt {
1355 ($(impl $name: path)+) => {$(
1356 impl $name for I24 {
1357 #[inline]
1358 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1359 <u32 as $name>::fmt(self.as_bits(), f)
1360 }
1361 }
1362 )*};
1363}
1364
1365impl_fmt! {
1366 impl Display
1367 impl Debug
1368}
1369
1370impl UpperHex for I24 {
1372 #[inline]
1373 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1374 write!(f, "{:06X}", self.to_bits() & 0x00FF_FFFF)
1375 }
1376}
1377
1378impl LowerHex for I24 {
1379 #[inline]
1380 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1381 write!(f, "{:06x}", self.to_bits() & 0x00FF_FFFF)
1382 }
1383}
1384
1385impl_bits_fmt! {
1386 impl Octal
1387 impl fmt::Binary
1388}
1389
1390#[cfg(feature = "serde")]
1391mod serde {
1392 impl serde::Serialize for crate::I24 {
1393 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1394 where
1395 S: serde::Serializer,
1396 {
1397 serializer.serialize_i32(self.to_i32())
1398 }
1399 }
1400
1401 impl<'de> serde::Deserialize<'de> for crate::I24 {
1402 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1403 where
1404 D: serde::Deserializer<'de>,
1405 {
1406 deserializer.deserialize_any(I24Visitor)
1407 }
1408 }
1409
1410 struct I24Visitor;
1411
1412 macro_rules! impl_deserialize_infallible {
1413 ($([$ty: path, $visit: ident, $from: ident])+) => {$(
1414 fn $visit<E>(self, v: $ty) -> Result<Self::Value, E> {
1415 Ok(crate::I24::$from(v))
1416 }
1417 )*};
1418 }
1419
1420 macro_rules! impl_deserialize_fallible {
1421 ($([$ty: path, $visit: ident, $try_from: ident])+) => {$(
1422 fn $visit<E>(self, v: $ty) -> Result<Self::Value, E> where E: serde::de::Error {
1423 crate::I24::$try_from(v).ok_or_else(|| E::custom("I24 out of range!"))
1424 }
1425 )*};
1426 }
1427
1428 impl serde::de::Visitor<'_> for I24Visitor {
1429 type Value = crate::I24;
1430
1431 fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
1432 formatter.write_str("an integer between -2^23 and 2^23")
1433 }
1434
1435 impl_deserialize_infallible! {
1436 [u8, visit_u8, from_u8]
1437 [i8, visit_i8, from_i8]
1438 [u16, visit_u16, from_u16]
1439 [i16, visit_i16, from_i16]
1440 }
1441
1442 impl_deserialize_fallible! {
1443 [u32, visit_u32, try_from_u32]
1444 [i32, visit_i32, try_from_i32]
1445 [u64, visit_u64, try_from_u64]
1446 [i64, visit_i64, try_from_i64]
1447 [u128, visit_u128, try_from_u128]
1448 [i128, visit_i128, try_from_i128]
1449 }
1450 }
1451}
1452
1453const _: () = assert!(core::mem::size_of::<I24>() == core::mem::size_of::<I24Repr>());
1455const _: () = assert!(core::mem::align_of::<I24>() == core::mem::align_of::<I24Repr>());
1456
1457impl ToBytes for I24 {
1458 type Bytes = [u8; 3];
1459
1460 fn to_be_bytes(&self) -> Self::Bytes {
1461 self.0.to_be_bytes()
1462 }
1463
1464 fn to_le_bytes(&self) -> Self::Bytes {
1465 self.0.to_le_bytes()
1466 }
1467}
1468
1469impl ToPrimitive for I24 {
1470 #[inline]
1471 fn to_i64(&self) -> Option<i64> {
1472 Some(I24::to_i32(*self) as i64)
1473 }
1474
1475 #[inline]
1476 fn to_u64(&self) -> Option<u64> {
1477 let val = I24::to_i32(*self);
1478 if val < 0 { None } else { Some(val as u64) }
1479 }
1480
1481 #[inline]
1482 fn to_i32(&self) -> Option<i32> {
1483 Some(I24::to_i32(*self))
1484 }
1485
1486 #[inline]
1487 fn to_u32(&self) -> Option<u32> {
1488 let val = I24::to_i32(*self);
1489 if val < 0 { None } else { Some(val as u32) }
1490 }
1491
1492 #[inline]
1493 fn to_i16(&self) -> Option<i16> {
1494 let val = I24::to_i32(*self);
1495 if val < i16::MIN as i32 || val > i16::MAX as i32 {
1496 None
1497 } else {
1498 Some(val as i16)
1499 }
1500 }
1501
1502 #[inline]
1503 fn to_u16(&self) -> Option<u16> {
1504 let val = I24::to_i32(*self);
1505 if val < 0 || val > u16::MAX as i32 {
1506 None
1507 } else {
1508 Some(val as u16)
1509 }
1510 }
1511
1512 #[inline]
1513 fn to_i8(&self) -> Option<i8> {
1514 let val = I24::to_i32(*self);
1515 if val < i8::MIN as i32 || val > i8::MAX as i32 {
1516 None
1517 } else {
1518 Some(val as i8)
1519 }
1520 }
1521
1522 #[inline]
1523 fn to_u8(&self) -> Option<u8> {
1524 let val = I24::to_i32(*self);
1525 if val < 0 || val > u8::MAX as i32 {
1526 None
1527 } else {
1528 Some(val as u8)
1529 }
1530 }
1531
1532 #[inline]
1533 fn to_isize(&self) -> Option<isize> {
1534 Some(I24::to_i32(*self) as isize)
1535 }
1536
1537 #[inline]
1538 fn to_usize(&self) -> Option<usize> {
1539 let val = I24::to_i32(*self);
1540 if val < 0 { None } else { Some(val as usize) }
1541 }
1542
1543 #[inline]
1544 fn to_f32(&self) -> Option<f32> {
1545 Some(I24::to_i32(*self) as f32)
1546 }
1547
1548 #[inline]
1549 fn to_f64(&self) -> Option<f64> {
1550 Some(I24::to_i32(*self) as f64)
1551 }
1552
1553 #[inline]
1554 fn to_i128(&self) -> Option<i128> {
1555 Some(I24::to_i32(*self) as i128)
1556 }
1557
1558 #[inline]
1559 fn to_u128(&self) -> Option<u128> {
1560 let val = I24::to_i32(*self);
1561 if val < 0 { None } else { Some(val as u128) }
1562 }
1563}
1564
1565#[cfg(feature = "num-cast")]
1571impl NumCast for I24 {
1572 #[inline]
1598 fn from<T: ToPrimitive>(n: T) -> Option<Self> {
1599 n.to_i64().and_then(Self::try_from_i64)
1600 }
1601}
1602
1603#[cfg(feature = "ndarray")]
1604mod ndarray_support {
1605 impl ndarray::ScalarOperand for crate::I24 {}
1606 impl ndarray::ScalarOperand for crate::I24Bytes {}
1607}
1608
1609#[cfg(feature = "pyo3")]
1610pub(crate) mod python {
1611 extern crate std;
1612
1613 use crate::I24;
1614 use numpy::{Element, PyArrayDescr};
1615 use pyo3::Py;
1616 use pyo3::{
1617 conversion::{FromPyObject, IntoPyObject},
1618 prelude::*,
1619 sync::PyOnceLock,
1620 };
1621
1622 #[pyclass(name = "I24", frozen)]
1627 pub struct PyI24 {
1628 pub value: I24,
1630 }
1631
1632 impl PyI24 {
1633 pub fn new(value: I24) -> Self {
1635 PyI24 { value }
1636 }
1637 }
1638
1639 #[pymethods]
1640 impl PyI24 {
1641 #[new]
1642 fn py_new(value: i32) -> PyResult<Self> {
1643 match I24::try_from_i32(value) {
1644 Some(v) => Ok(PyI24 { value: v }),
1645 None => Err(pyo3::exceptions::PyValueError::new_err(format!(
1646 "Value {} is out of range for I24 (-8388608 to 8388607)",
1647 value
1648 ))),
1649 }
1650 }
1651
1652 #[staticmethod]
1653 fn from_bytes(bytes: &[u8], byteorder: &str) -> PyResult<Self> {
1654 if bytes.len() != 3 {
1655 return Err(pyo3::exceptions::PyValueError::new_err(
1656 "bytes must be exactly 3 bytes long",
1657 ));
1658 }
1659 let byte_array: [u8; 3] = [bytes[0], bytes[1], bytes[2]];
1660 let value = match byteorder {
1661 "little" => I24::from_le_bytes(byte_array),
1662 "big" => I24::from_be_bytes(byte_array),
1663 "native" => I24::from_ne_bytes(byte_array),
1664 _ => {
1665 return Err(pyo3::exceptions::PyValueError::new_err(
1666 "byteorder must be 'little', 'big', or 'native'",
1667 ));
1668 }
1669 };
1670 Ok(PyI24 { value })
1671 }
1672
1673 fn to_int(&self) -> i32 {
1674 self.value.to_i32()
1675 }
1676
1677 fn to_bytes(&self, byteorder: &str) -> PyResult<Vec<u8>> {
1678 let bytes = match byteorder {
1679 "little" => self.value.to_le_bytes(),
1680 "big" => self.value.to_be_bytes(),
1681 "native" => self.value.to_ne_bytes(),
1682 _ => {
1683 return Err(pyo3::exceptions::PyValueError::new_err(
1684 "byteorder must be 'little', 'big', or 'native'",
1685 ));
1686 }
1687 };
1688 Ok(bytes.to_vec())
1689 }
1690
1691 fn __str__(&self) -> String {
1692 format!("{}", self.value.to_i32())
1693 }
1694
1695 fn __repr__(&self) -> String {
1696 format!("I24({})", self.value.to_i32())
1697 }
1698
1699 fn __int__(&self) -> i32 {
1700 self.value.to_i32()
1701 }
1702
1703 fn __eq__(&self, other: &PyI24) -> bool {
1705 self.value == other.value
1706 }
1707
1708 fn __ne__(&self, other: &PyI24) -> bool {
1709 self.value != other.value
1710 }
1711
1712 fn __lt__(&self, other: &PyI24) -> bool {
1713 self.value < other.value
1714 }
1715
1716 fn __le__(&self, other: &PyI24) -> bool {
1717 self.value <= other.value
1718 }
1719
1720 fn __gt__(&self, other: &PyI24) -> bool {
1721 self.value > other.value
1722 }
1723
1724 fn __ge__(&self, other: &PyI24) -> bool {
1725 self.value >= other.value
1726 }
1727
1728 fn __eq_int__(&self, other: i32) -> bool {
1730 self.value.to_i32() == other
1731 }
1732
1733 fn __ne_int__(&self, other: i32) -> bool {
1734 self.value.to_i32() != other
1735 }
1736
1737 fn __lt_int__(&self, other: i32) -> bool {
1738 self.value.to_i32() < other
1739 }
1740
1741 fn __le_int__(&self, other: i32) -> bool {
1742 self.value.to_i32() <= other
1743 }
1744
1745 fn __gt_int__(&self, other: i32) -> bool {
1746 self.value.to_i32() > other
1747 }
1748
1749 fn __ge_int__(&self, other: i32) -> bool {
1750 self.value.to_i32() >= other
1751 }
1752
1753 fn __add__(&self, other: &PyI24) -> PyResult<PyI24> {
1755 match self.value.checked_add(other.value) {
1756 Some(result) => Ok(PyI24 { value: result }),
1757 None => Err(pyo3::exceptions::PyOverflowError::new_err(
1758 "I24 addition overflow",
1759 )),
1760 }
1761 }
1762
1763 fn __sub__(&self, other: &PyI24) -> PyResult<PyI24> {
1764 match self.value.checked_sub(other.value) {
1765 Some(result) => Ok(PyI24 { value: result }),
1766 None => Err(pyo3::exceptions::PyOverflowError::new_err(
1767 "I24 subtraction overflow",
1768 )),
1769 }
1770 }
1771
1772 fn __mul__(&self, other: &PyI24) -> PyResult<PyI24> {
1773 match self.value.checked_mul(other.value) {
1774 Some(result) => Ok(PyI24 { value: result }),
1775 None => Err(pyo3::exceptions::PyOverflowError::new_err(
1776 "I24 multiplication overflow",
1777 )),
1778 }
1779 }
1780
1781 fn __truediv__(&self, other: &PyI24) -> PyResult<f64> {
1782 if other.value.to_i32() == 0 {
1783 return Err(pyo3::exceptions::PyZeroDivisionError::new_err(
1784 "division by zero",
1785 ));
1786 }
1787 Ok(self.value.to_i32() as f64 / other.value.to_i32() as f64)
1788 }
1789
1790 fn __floordiv__(&self, other: &PyI24) -> PyResult<PyI24> {
1791 match self.value.checked_div(other.value) {
1792 Some(result) => Ok(PyI24 { value: result }),
1793 None => Err(pyo3::exceptions::PyZeroDivisionError::new_err(
1794 "division by zero",
1795 )),
1796 }
1797 }
1798
1799 fn __mod__(&self, other: &PyI24) -> PyResult<PyI24> {
1800 match self.value.checked_rem(other.value) {
1801 Some(result) => Ok(PyI24 { value: result }),
1802 None => Err(pyo3::exceptions::PyZeroDivisionError::new_err(
1803 "division by zero",
1804 )),
1805 }
1806 }
1807
1808 fn __and__(&self, other: &PyI24) -> PyI24 {
1810 PyI24 {
1811 value: self.value & other.value,
1812 }
1813 }
1814
1815 fn __or__(&self, other: &PyI24) -> PyI24 {
1816 PyI24 {
1817 value: self.value | other.value,
1818 }
1819 }
1820
1821 fn __xor__(&self, other: &PyI24) -> PyI24 {
1822 PyI24 {
1823 value: self.value ^ other.value,
1824 }
1825 }
1826
1827 fn __lshift__(&self, other: u32) -> PyResult<PyI24> {
1828 if other >= 32 {
1829 return Err(pyo3::exceptions::PyValueError::new_err(
1830 "shift count out of range",
1831 ));
1832 }
1833 let result = (self.value.to_i32() as u32) << other;
1834 match I24::try_from_i32(result as i32) {
1835 Some(val) => Ok(PyI24 { value: val }),
1836 None => Err(pyo3::exceptions::PyOverflowError::new_err(
1837 "I24 left shift overflow",
1838 )),
1839 }
1840 }
1841
1842 fn __rshift__(&self, other: u32) -> PyResult<PyI24> {
1843 if other >= 32 {
1844 return Err(pyo3::exceptions::PyValueError::new_err(
1845 "shift count out of range",
1846 ));
1847 }
1848 let result = self.value.to_i32() >> other;
1849 Ok(PyI24 {
1850 value: I24::wrapping_from_i32(result),
1851 })
1852 }
1853
1854 #[staticmethod]
1856 fn min_value() -> PyI24 {
1857 PyI24 { value: I24::MIN }
1858 }
1859
1860 #[staticmethod]
1861 fn max_value() -> PyI24 {
1862 PyI24 { value: I24::MAX }
1863 }
1864
1865 fn count_ones(&self) -> u32 {
1866 self.value.to_i32().count_ones()
1867 }
1868
1869 fn count_zeros(&self) -> u32 {
1870 self.value.to_i32().count_zeros()
1871 }
1872
1873 fn leading_zeros(&self) -> u32 {
1874 self.value.to_i32().leading_zeros()
1875 }
1876
1877 fn trailing_zeros(&self) -> u32 {
1878 self.value.to_i32().trailing_zeros()
1879 }
1880
1881 fn checked_add(&self, other: &PyI24) -> Option<PyI24> {
1883 self.value
1884 .checked_add(other.value)
1885 .map(|v| PyI24 { value: v })
1886 }
1887
1888 fn checked_sub(&self, other: &PyI24) -> Option<PyI24> {
1889 self.value
1890 .checked_sub(other.value)
1891 .map(|v| PyI24 { value: v })
1892 }
1893
1894 fn checked_mul(&self, other: &PyI24) -> Option<PyI24> {
1895 self.value
1896 .checked_mul(other.value)
1897 .map(|v| PyI24 { value: v })
1898 }
1899
1900 fn checked_div(&self, other: &PyI24) -> Option<PyI24> {
1901 self.value
1902 .checked_div(other.value)
1903 .map(|v| PyI24 { value: v })
1904 }
1905
1906 fn wrapping_add(&self, other: &PyI24) -> PyI24 {
1907 let result = self.value.to_i32().wrapping_add(other.value.to_i32());
1908 PyI24 {
1909 value: I24::wrapping_from_i32(result),
1910 }
1911 }
1912
1913 fn wrapping_sub(&self, other: &PyI24) -> PyI24 {
1914 let result = self.value.to_i32().wrapping_sub(other.value.to_i32());
1915 PyI24 {
1916 value: I24::wrapping_from_i32(result),
1917 }
1918 }
1919
1920 fn wrapping_mul(&self, other: &PyI24) -> PyI24 {
1921 let result = self.value.to_i32().wrapping_mul(other.value.to_i32());
1922 PyI24 {
1923 value: I24::wrapping_from_i32(result),
1924 }
1925 }
1926
1927 fn saturating_add(&self, other: &PyI24) -> PyI24 {
1928 let result = self.value.to_i32().saturating_add(other.value.to_i32());
1929 PyI24 {
1930 value: I24::saturating_from_i32(result),
1931 }
1932 }
1933
1934 fn saturating_sub(&self, other: &PyI24) -> PyI24 {
1935 let result = self.value.to_i32().saturating_sub(other.value.to_i32());
1936 PyI24 {
1937 value: I24::saturating_from_i32(result),
1938 }
1939 }
1940
1941 fn saturating_mul(&self, other: &PyI24) -> PyI24 {
1942 let result = self.value.to_i32().saturating_mul(other.value.to_i32());
1943 PyI24 {
1944 value: I24::saturating_from_i32(result),
1945 }
1946 }
1947
1948 fn __hash__(&self) -> u64 {
1950 use std::collections::hash_map::DefaultHasher;
1951 use std::hash::{Hash, Hasher};
1952 let mut hasher = DefaultHasher::new();
1953 self.value.hash(&mut hasher);
1954 hasher.finish()
1955 }
1956
1957 fn __abs__(&self) -> PyResult<PyI24> {
1958 let abs_val = self.value.to_i32().abs();
1959 match I24::try_from_i32(abs_val) {
1960 Some(val) => Ok(PyI24 { value: val }),
1961 None => Err(pyo3::exceptions::PyOverflowError::new_err(
1962 "Absolute value overflow for I24::MIN",
1963 )),
1964 }
1965 }
1966
1967 fn __neg__(&self) -> PyResult<PyI24> {
1968 match self.value.checked_neg() {
1969 Some(result) => Ok(PyI24 { value: result }),
1970 None => Err(pyo3::exceptions::PyOverflowError::new_err(
1971 "I24 negation overflow",
1972 )),
1973 }
1974 }
1975
1976 fn __pos__(&self) -> PyI24 {
1977 PyI24 { value: self.value }
1978 }
1979
1980 fn __invert__(&self) -> PyI24 {
1981 let inverted = !self.value;
1982 PyI24 { value: inverted }
1983 }
1984
1985 fn bit_length(&self) -> u32 {
1987 let val = if self.value.to_i32() < 0 {
1988 (!self.value.to_i32()) as u32
1989 } else {
1990 self.value.to_i32() as u32
1991 };
1992 32 - val.leading_zeros()
1993 }
1994
1995 fn bit_count(&self) -> u32 {
1996 let abs_val = self.value.to_i32().unsigned_abs();
1997 abs_val.count_ones()
1998 }
1999
2000 fn as_integer_ratio(&self) -> (i32, i32) {
2001 (self.value.to_i32(), 1)
2002 }
2003
2004 #[pyo3(signature = (ndigits = None))]
2005 fn __round__(&self, ndigits: Option<i32>) -> PyResult<PyI24> {
2006 match ndigits {
2007 None => Ok(PyI24 { value: self.value }),
2008 Some(0) => Ok(PyI24 { value: self.value }),
2009 Some(_) => Ok(PyI24 { value: self.value }),
2010 }
2011 }
2012
2013 fn __ceil__(&self) -> PyI24 {
2014 PyI24 { value: self.value }
2015 }
2016
2017 fn __floor__(&self) -> PyI24 {
2018 PyI24 { value: self.value }
2019 }
2020
2021 fn __trunc__(&self) -> PyI24 {
2022 PyI24 { value: self.value }
2023 }
2024 }
2025
2026 unsafe impl Element for I24 {
2027 const IS_COPY: bool = true;
2028
2029 fn clone_ref(&self, _py: Python<'_>) -> Self {
2030 *self
2031 }
2032
2033 fn get_dtype(py: Python<'_>) -> Bound<'_, PyArrayDescr> {
2034 static DTYPE: PyOnceLock<Py<PyArrayDescr>> = PyOnceLock::new();
2041
2042 DTYPE
2043 .get_or_init(py, || {
2044 PyArrayDescr::new(py, "V4")
2045 .expect("Failed to construct NumPy dtype for I24")
2046 .unbind()
2047 })
2048 .clone_ref(py)
2049 .into_bound(py)
2050 }
2051 }
2052
2053 impl<'py> IntoPyObject<'py> for I24 {
2055 type Target = pyo3::types::PyInt;
2056 type Output = Bound<'py, pyo3::types::PyInt>;
2057 type Error = pyo3::PyErr;
2058
2059 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
2060 Ok(self.to_i32().into_pyobject(py)?)
2061 }
2062 }
2063
2064 impl<'py> IntoPyObject<'py> for &I24 {
2065 type Target = pyo3::types::PyInt;
2066 type Output = Bound<'py, pyo3::types::PyInt>;
2067 type Error = pyo3::PyErr;
2068
2069 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
2070 Ok(self.to_i32().into_pyobject(py)?)
2071 }
2072 }
2073
2074 impl<'a, 'py> FromPyObject<'a, 'py> for I24 {
2076 type Error = pyo3::PyErr;
2077
2078 fn extract(obj: pyo3::Borrowed<'a, 'py, pyo3::PyAny>) -> PyResult<Self> {
2079 let py_int: i32 = obj.extract()?;
2080 I24::try_from_i32(py_int).ok_or_else(|| {
2081 pyo3::exceptions::PyOverflowError::new_err(format!(
2082 "Value {} is out of range for I24 (-8388608 to 8388607)",
2083 py_int
2084 ))
2085 })
2086 }
2087 }
2088}
2089
2090#[repr(transparent)]
2119#[derive(Copy, Clone, Eq, PartialEq, Debug)]
2120#[cfg_attr(
2121 feature = "zerocopy",
2122 derive(zerocopy::FromBytes, zerocopy::Unaligned, zerocopy::IntoBytes)
2123)]
2124pub struct I24Bytes(pub [u8; 3]);
2125
2126unsafe impl Pod for I24Bytes {}
2128
2129unsafe impl Zeroable for I24Bytes {}
2131
2132impl I24Bytes {
2133 #[inline]
2145 pub const fn to_i24_le(self) -> I24 {
2146 I24::from_le_bytes(self.0)
2147 }
2148
2149 #[inline]
2161 pub const fn to_i24_be(self) -> I24 {
2162 I24::from_be_bytes(self.0)
2163 }
2164
2165 #[inline]
2178 pub const fn to_i24_ne(self) -> I24 {
2179 I24::from_ne_bytes(self.0)
2180 }
2181
2182 #[inline]
2194 pub const fn from_i24_le(value: I24) -> Self {
2195 Self(value.to_le_bytes())
2196 }
2197
2198 #[inline]
2210 pub const fn from_i24_be(value: I24) -> Self {
2211 Self(value.to_be_bytes())
2212 }
2213
2214 #[inline]
2226 pub const fn from_i24_ne(value: I24) -> Self {
2227 Self(value.to_ne_bytes())
2228 }
2229
2230 #[inline]
2244 pub const fn from_bytes(bytes: [u8; 3]) -> Self {
2245 Self(bytes)
2246 }
2247
2248 #[inline]
2259 pub const fn to_bytes(self) -> [u8; 3] {
2260 self.0
2261 }
2262}
2263
2264impl AsRef<[u8; 3]> for I24Bytes {
2266 #[inline]
2267 fn as_ref(&self) -> &[u8; 3] {
2268 &self.0
2269 }
2270}
2271
2272impl AsMut<[u8; 3]> for I24Bytes {
2273 #[inline]
2274 fn as_mut(&mut self) -> &mut [u8; 3] {
2275 &mut self.0
2276 }
2277}
2278
2279impl AsRef<[u8]> for I24Bytes {
2280 #[inline]
2281 fn as_ref(&self) -> &[u8] {
2282 &self.0[..]
2283 }
2284}
2285
2286impl AsMut<[u8]> for I24Bytes {
2287 #[inline]
2288 fn as_mut(&mut self) -> &mut [u8] {
2289 &mut self.0[..]
2290 }
2291}
2292
2293impl From<[u8; 3]> for I24Bytes {
2294 #[inline]
2295 fn from(bytes: [u8; 3]) -> Self {
2296 Self(bytes)
2297 }
2298}
2299
2300impl From<I24Bytes> for [u8; 3] {
2301 #[inline]
2302 fn from(i24_bytes: I24Bytes) -> Self {
2303 i24_bytes.0
2304 }
2305}
2306
2307impl core::ops::Deref for I24Bytes {
2308 type Target = [u8; 3];
2309
2310 #[inline]
2311 fn deref(&self) -> &Self::Target {
2312 &self.0
2313 }
2314}
2315
2316impl core::ops::DerefMut for I24Bytes {
2317 #[inline]
2318 fn deref_mut(&mut self) -> &mut Self::Target {
2319 &mut self.0
2320 }
2321}
2322
2323#[cfg(feature = "pyo3")]
2325mod i24_bytes_pyo3 {
2326 use super::I24Bytes;
2327 use pyo3::{
2328 conversion::{FromPyObject, IntoPyObject},
2329 prelude::*,
2330 };
2331
2332 impl<'py> IntoPyObject<'py> for I24Bytes {
2334 type Target = pyo3::types::PyBytes;
2335 type Output = Bound<'py, pyo3::types::PyBytes>;
2336 type Error = pyo3::PyErr;
2337
2338 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
2339 Ok(pyo3::types::PyBytes::new(py, &self.0))
2340 }
2341 }
2342
2343 impl<'py> IntoPyObject<'py> for &I24Bytes {
2344 type Target = pyo3::types::PyBytes;
2345 type Output = Bound<'py, pyo3::types::PyBytes>;
2346 type Error = pyo3::PyErr;
2347
2348 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
2349 Ok(pyo3::types::PyBytes::new(py, &self.0))
2350 }
2351 }
2352
2353 impl<'a, 'py> FromPyObject<'a, 'py> for I24Bytes {
2355 type Error = pyo3::PyErr;
2356
2357 fn extract(obj: pyo3::Borrowed<'a, 'py, pyo3::PyAny>) -> PyResult<Self> {
2358 let py_bytes: &[u8] = obj.extract()?;
2359 if py_bytes.len() != 3 {
2360 return Err(pyo3::exceptions::PyValueError::new_err(format!(
2361 "Expected exactly 3 bytes for I24Bytes, got {}",
2362 py_bytes.len()
2363 )));
2364 }
2365 Ok(I24Bytes([py_bytes[0], py_bytes[1], py_bytes[2]]))
2366 }
2367 }
2368}
2369
2370#[cfg(test)]
2371mod i24_tests {
2372 extern crate std;
2373
2374 use super::*;
2375 use std::format;
2376 use std::num::IntErrorKind;
2377
2378 #[test]
2379 fn test_arithmetic_operations() {
2380 let a = i24!(100);
2381 let b = i24!(50);
2382
2383 assert_eq!(a + b, i24!(150));
2384 assert_eq!(a - b, i24!(50));
2385 assert_eq!(a * b, i24!(5000));
2386 assert_eq!(a / b, i24!(2));
2387 assert_eq!(a % b, i24!(0));
2388 }
2389
2390 #[test]
2391 fn test_negative_operations() {
2392 let a = i24!(100);
2393 let b = i24!(-50);
2394
2395 assert_eq!(a + b, i24!(50));
2396 assert_eq!(a - b, i24!(150));
2397 assert_eq!(a * b, i24!(-5000));
2398 assert_eq!(a / b, i24!(-2));
2399 }
2400
2401 #[test]
2402 fn test_bitwise_operations() {
2403 let a = i24!(0b101010);
2404 let b = i24!(0b110011);
2405
2406 assert_eq!(a & b, i24!(0b100010));
2407 assert_eq!(a | b, i24!(0b111011));
2408 assert_eq!(a ^ b, i24!(0b011001));
2409 assert_eq!(a << 2, i24!(0b10101000));
2410 assert_eq!(a >> 2, i24!(0b1010));
2411 }
2412
2413 #[test]
2414 fn test_checked_addition() {
2415 assert_eq!(i24!(10).checked_add(i24!(20)), Some(i24!(30)));
2416 assert_eq!(i24!(10).checked_add(i24!(-20)), Some(i24!(-10)));
2417 assert_eq!(I24::MAX.checked_add(I24::one()), None);
2419 assert_eq!(
2420 (I24::MAX - I24::one()).checked_add(I24::one() * i24!(2)),
2421 None
2422 );
2423 }
2424
2425 #[test]
2426 fn test_checked_subtraction() {
2427 assert_eq!(i24!(10).checked_sub(i24!(20)), Some(i24!(-10)));
2428 assert_eq!(i24!(10).checked_sub(i24!(-20)), Some(i24!(30)));
2429
2430 assert_eq!(I24::MIN.checked_sub(I24::one()), None);
2432 assert_eq!(
2433 (I24::MIN + I24::one()).checked_sub(I24::one() * i24!(2)),
2434 None
2435 );
2436 }
2437
2438 #[test]
2439 fn test_checked_division() {
2440 assert_eq!(i24!(20).checked_div(i24!(5)), Some(i24!(4)));
2441 assert_eq!(i24!(20).checked_div(i24!(0)), None);
2442 }
2443
2444 #[test]
2445 fn test_checked_multiplication() {
2446 assert_eq!(i24!(5).checked_mul(i24!(6)), Some(i24!(30)));
2447 assert_eq!(I24::MAX.checked_mul(i24!(2)), None);
2448 }
2449
2450 #[test]
2451 fn test_checked_remainder() {
2452 assert_eq!(i24!(20).checked_rem(i24!(5)), Some(i24!(0)));
2453 assert_eq!(i24!(20).checked_rem(i24!(0)), None);
2454 }
2455
2456 #[test]
2457 fn test_unary_operations() {
2458 let a = i24!(100);
2459
2460 assert_eq!(-a, i24!(-100));
2461 assert_eq!(!a, i24!(-101));
2462 }
2463
2464 #[test]
2465 fn test_from_bytes() {
2466 let le = i24!(0x030201);
2467 let be = i24!(0x010203);
2468 assert_eq!(
2469 I24::from_ne_bytes([0x01, 0x02, 0x03]),
2470 if cfg!(target_endian = "big") { be } else { le }
2471 );
2472 assert_eq!(I24::from_le_bytes([0x01, 0x02, 0x03]), le);
2473 assert_eq!(I24::from_be_bytes([0x01, 0x02, 0x03]), be);
2474 }
2475
2476 #[test]
2477 fn test_zero_and_one() {
2478 assert_eq!(
2479 I24::zero(),
2480 I24::try_from_i32(0).expect("Zero should convert successfully")
2481 );
2482
2483 assert_eq!(I24::zero(), i24!(0));
2484 assert_eq!(I24::one(), i24!(1));
2485 }
2486
2487 #[test]
2488 fn test_from_str() {
2489 assert_eq!(
2490 I24::from_str("100").expect("100 should parse successfully"),
2491 i24!(100)
2492 );
2493 assert_eq!(
2494 I24::from_str("-100").expect("-100 should parse successfully"),
2495 i24!(-100)
2496 );
2497 assert_eq!(
2498 I24::from_str(&format!("{}", I24::MAX)).expect("MAX should parse successfully"),
2499 I24::MAX
2500 );
2501 assert_eq!(
2502 I24::from_str(&format!("{}", I24::MIN)).expect("MIN should parse successfully"),
2503 I24::MIN
2504 );
2505 assert_eq!(
2506 *I24::from_str("8388608")
2507 .expect_err("Expected parse error")
2508 .kind(),
2509 IntErrorKind::PosOverflow
2510 );
2511 assert_eq!(
2512 *I24::from_str("-8388609")
2513 .expect_err("Expected parse error")
2514 .kind(),
2515 IntErrorKind::NegOverflow
2516 );
2517 }
2518
2519 #[test]
2520 fn test_display() {
2521 assert_eq!(format!("{}", i24!(100)), "100");
2522 assert_eq!(format!("{}", i24!(-100)), "-100");
2523 }
2524
2525 #[test]
2526 fn test_wrapping_behavior() {
2527 assert_eq!(I24::MAX + I24::one(), I24::MIN);
2528 assert_eq!(I24::MAX + I24::one() + I24::one(), I24::MIN + I24::one());
2529
2530 assert_eq!(I24::MIN - I24::one(), I24::MAX);
2531 assert_eq!(I24::MIN - (I24::one() + I24::one()), I24::MAX - I24::one());
2532
2533 assert_eq!(-I24::MIN, I24::MIN)
2534 }
2535
2536 #[test]
2537 fn discriminant_optimization() {
2538 assert_eq!(size_of::<I24>(), size_of::<Option<I24>>());
2542 assert_eq!(size_of::<I24>(), size_of::<Option<Option<I24>>>());
2543 assert_eq!(size_of::<I24>(), size_of::<Option<Option<Option<I24>>>>());
2544 assert_eq!(
2545 size_of::<I24>(),
2546 size_of::<Option<Option<Option<Option<I24>>>>>()
2547 );
2548 }
2549
2550 #[test]
2551 fn test_shift_operations() {
2552 let a = i24!(0b1);
2553
2554 assert_eq!(a << 23, I24::MIN); assert_eq!(a << 24, a); let b = i24!(-1); assert_eq!(b >> 1, i24!(-1)); assert_eq!(b >> 23, i24!(-1)); assert_eq!(b >> 24, i24!(-1)); let c = i24!(0x7FFFFF); assert_eq!(c << 1, i24!(-2)); let d = I24::MIN; assert_eq!(d >> 1, i24!(-0x400000));
2571 assert_eq!(d >> 2, i24!(-0x200000));
2572 assert_eq!(d >> 3, i24!(-0x100000));
2573 assert_eq!(d >> 4, i24!(-0x080000));
2574
2575 assert_eq!(c << 1, i24!(-2)); assert_eq!(c << 2, i24!(-4)); assert_eq!(c << 3, i24!(-8)); }
2580
2581 #[test]
2582 fn test_to_from_i32() {
2583 for i in I24Repr::MIN..=I24Repr::MAX {
2584 assert_eq!(
2585 I24::try_from_i32(i)
2586 .expect("Value in range should convert successfully")
2587 .to_i32(),
2588 i
2589 )
2590 }
2591 }
2592
2593 #[test]
2594 fn test_from() {
2595 macro_rules! impl_t {
2596 ($($ty: ty),+) => {{$(
2597 for x in <$ty>::MIN..=<$ty>::MAX {
2598 assert_eq!(<$ty>::try_from(<I24 as From<$ty>>::from(x).to_i32()).expect("Value should convert back"), x)
2599 }
2600 )+}};
2601 }
2602
2603 assert_eq!(<I24 as From<bool>>::from(true), I24::one());
2604 assert_eq!(<I24 as From<bool>>::from(false), I24::zero());
2605
2606 impl_t!(i8, i16, u8, u16)
2607 }
2608
2609 #[test]
2610 fn test_try_from() {
2611 macro_rules! impl_t {
2612 (signed $($ty: ty),+) => {{$(
2613 for x in I24Repr::MIN..=I24Repr::MAX {
2614 assert_eq!(I24::try_from(<$ty as From<i32>>::from(x)).expect("Value should convert successfully").to_i32(), x)
2615 }
2616 )+}};
2617
2618 (unsigned $($ty: ty),+) => {{$(
2619 for x in 0..=I24Repr::MAX {
2620 assert_eq!(I24::try_from(<$ty>::try_from(x).expect("Value should fit in type")).expect("Value should convert to I24").to_i32(), x)
2621 }
2622 )+}};
2623 }
2624
2625 impl_t!(signed i32, i64, i128);
2626 impl_t!(unsigned u32, u64, u128);
2627 }
2628
2629 #[test]
2630 fn test_to_from_bits() {
2631 for i in 0..(1 << 24) {
2632 assert_eq!(I24::from_bits_truncate(i).to_bits(), i)
2633 }
2634 }
2635
2636 #[test]
2637 #[cfg(feature = "serde")]
2638 fn test_deserialize_json() {
2639 #[derive(Debug, PartialEq, ::serde::Deserialize)]
2640 struct TestStruct {
2641 value: I24,
2642 }
2643
2644 let test: TestStruct =
2645 serde_json::from_str("{ \"value\": 11 }").expect("Failed to deserialize!");
2646 let expected = TestStruct {
2647 value: I24::from_u8(11),
2648 };
2649
2650 assert_eq!(test, expected);
2651 }
2652
2653 #[test]
2654 #[cfg(feature = "serde")]
2655 fn test_serialize_json() {
2656 #[derive(Debug, PartialEq, ::serde::Serialize)]
2657 struct TestStruct {
2658 value: I24,
2659 }
2660
2661 let test_struct = TestStruct {
2662 value: I24::from_u8(11),
2663 };
2664 let test = serde_json::to_string(&test_struct).expect("Failed to serialize!");
2665 assert_eq!(test, "{\"value\":11}");
2666 }
2667
2668 #[test]
2669 fn test_to_primitive_signed() {
2670 use num_traits::ToPrimitive;
2671
2672 let val = i24!(100);
2674 assert_eq!(val.to_i8(), Some(100i8));
2675 assert_eq!(val.to_i16(), Some(100i16));
2676 assert_eq!(ToPrimitive::to_i32(&val), Some(100i32));
2677 assert_eq!(val.to_i64(), Some(100i64));
2678 assert_eq!(val.to_i128(), Some(100i128));
2679 assert_eq!(val.to_isize(), Some(100isize));
2680
2681 let val = i24!(-100);
2683 assert_eq!(val.to_i8(), Some(-100i8));
2684 assert_eq!(val.to_i16(), Some(-100i16));
2685 assert_eq!(ToPrimitive::to_i32(&val), Some(-100i32));
2686 assert_eq!(val.to_i64(), Some(-100i64));
2687 assert_eq!(val.to_i128(), Some(-100i128));
2688 assert_eq!(val.to_isize(), Some(-100isize));
2689
2690 let val = I24::MAX;
2692 assert_eq!(val.to_i8(), None); assert_eq!(val.to_i16(), None); assert_eq!(ToPrimitive::to_i32(&val), Some(I24::MAX.to_i32()));
2695
2696 let val = I24::MIN;
2697 assert_eq!(val.to_i8(), None); assert_eq!(val.to_i16(), None); assert_eq!(ToPrimitive::to_i32(&val), Some(I24::MIN.to_i32()));
2700 }
2701
2702 #[test]
2703 fn test_to_primitive_unsigned() {
2704 use num_traits::ToPrimitive;
2705
2706 let val = i24!(100);
2708 assert_eq!(val.to_u8(), Some(100u8));
2709 assert_eq!(val.to_u16(), Some(100u16));
2710 assert_eq!(val.to_u32(), Some(100u32));
2711 assert_eq!(val.to_u64(), Some(100u64));
2712 assert_eq!(val.to_u128(), Some(100u128));
2713 assert_eq!(val.to_usize(), Some(100usize));
2714
2715 let val = i24!(-100);
2717 assert_eq!(val.to_u8(), None);
2718 assert_eq!(val.to_u16(), None);
2719 assert_eq!(val.to_u32(), None);
2720 assert_eq!(val.to_u64(), None);
2721 assert_eq!(val.to_u128(), None);
2722 assert_eq!(val.to_usize(), None);
2723
2724 let val = I24::MAX;
2726 assert_eq!(val.to_u8(), None); assert_eq!(val.to_u16(), None); assert_eq!(val.to_u32(), Some(I24::MAX.to_i32() as u32));
2729
2730 let val = i24!(0);
2732 assert_eq!(val.to_u8(), Some(0u8));
2733 assert_eq!(val.to_u16(), Some(0u16));
2734 assert_eq!(val.to_u32(), Some(0u32));
2735 assert_eq!(val.to_u64(), Some(0u64));
2736 assert_eq!(val.to_u128(), Some(0u128));
2737 assert_eq!(val.to_usize(), Some(0usize));
2738 }
2739
2740 #[test]
2741 fn test_to_primitive_floats() {
2742 use num_traits::ToPrimitive;
2743
2744 let val = i24!(100);
2746 assert_eq!(val.to_f32(), Some(100.0f32));
2747 assert_eq!(val.to_f64(), Some(100.0f64));
2748
2749 let val = i24!(-100);
2751 assert_eq!(val.to_f32(), Some(-100.0f32));
2752 assert_eq!(val.to_f64(), Some(-100.0f64));
2753
2754 let val = I24::MAX;
2756 assert_eq!(val.to_f32(), Some(I24::MAX.to_i32() as f32));
2757 assert_eq!(val.to_f64(), Some(I24::MAX.to_i32() as f64));
2758
2759 let val = I24::MIN;
2760 assert_eq!(val.to_f32(), Some(I24::MIN.to_i32() as f32));
2761 assert_eq!(val.to_f64(), Some(I24::MIN.to_i32() as f64));
2762 }
2763
2764 #[test]
2765 fn test_to_primitive_boundary_values() {
2766 use num_traits::ToPrimitive;
2767
2768 let val = i24!(127); assert_eq!(val.to_i8(), Some(127i8));
2771 assert_eq!(val.to_u8(), Some(127u8));
2772
2773 let val = i24!(128); assert_eq!(val.to_i8(), None);
2775 assert_eq!(val.to_u8(), Some(128u8));
2776
2777 let val = i24!(255); assert_eq!(val.to_u8(), Some(255u8));
2779
2780 let val = i24!(256); assert_eq!(val.to_u8(), None);
2782
2783 let val = i24!(32767); assert_eq!(val.to_i16(), Some(32767i16));
2785 assert_eq!(val.to_u16(), Some(32767u16));
2786
2787 let val = i24!(32768); assert_eq!(val.to_i16(), None);
2789 assert_eq!(val.to_u16(), Some(32768u16));
2790
2791 let val = i24!(65535); assert_eq!(val.to_u16(), Some(65535u16));
2793
2794 let val = i24!(65536); assert_eq!(val.to_u16(), None);
2796 }
2797
2798 #[cfg(feature = "alloc")]
2799 #[test]
2800 fn test_packed_struct_utilities() {
2801 use crate::PackedStruct;
2802 use alloc::vec;
2803 use alloc::vec::Vec;
2804
2805 #[derive(Debug, Clone, PartialEq)]
2807 struct TestDataStruct {
2808 t: u32,
2809 ch1: I24,
2810 ch2: I24,
2811 ch3: I24,
2812 ch4: I24,
2813 s: I24,
2814 }
2815
2816 impl PackedStruct for TestDataStruct {
2817 const PACKED_SIZE: usize = 4 + 5 * 3; fn from_packed_bytes(bytes: &[u8]) -> Option<Self> {
2820 if bytes.len() < Self::PACKED_SIZE {
2821 return None;
2822 }
2823
2824 let t = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
2825 let ch1 = I24::from_le_bytes([bytes[4], bytes[5], bytes[6]]);
2826 let ch2 = I24::from_le_bytes([bytes[7], bytes[8], bytes[9]]);
2827 let ch3 = I24::from_le_bytes([bytes[10], bytes[11], bytes[12]]);
2828 let ch4 = I24::from_le_bytes([bytes[13], bytes[14], bytes[15]]);
2829 let s = I24::from_le_bytes([bytes[16], bytes[17], bytes[18]]);
2830
2831 Some(TestDataStruct {
2832 t,
2833 ch1,
2834 ch2,
2835 ch3,
2836 ch4,
2837 s,
2838 })
2839 }
2840
2841 fn to_packed_bytes(&self) -> Vec<u8> {
2842 let mut bytes = Vec::with_capacity(Self::PACKED_SIZE);
2843 bytes.extend_from_slice(&self.t.to_le_bytes());
2844 bytes.extend_from_slice(&self.ch1.to_le_bytes());
2845 bytes.extend_from_slice(&self.ch2.to_le_bytes());
2846 bytes.extend_from_slice(&self.ch3.to_le_bytes());
2847 bytes.extend_from_slice(&self.ch4.to_le_bytes());
2848 bytes.extend_from_slice(&self.s.to_le_bytes());
2849 bytes
2850 }
2851 }
2852
2853 let original = TestDataStruct {
2855 t: 0x12345678,
2856 ch1: I24::try_from_i32(0x123456).expect("Test value should convert successfully"),
2857 ch2: I24::try_from_i32(-1000).expect("Test value should convert successfully"),
2858 ch3: I24::try_from_i32(0).expect("Test value should convert successfully"),
2859 ch4: I24::try_from_i32(8388607).expect("Test value should convert successfully"), s: I24::try_from_i32(-8388608).expect("Test value should convert successfully"), };
2862
2863 let packed_bytes = original.to_packed_bytes();
2865 assert_eq!(packed_bytes.len(), TestDataStruct::PACKED_SIZE);
2866
2867 let deserialized = TestDataStruct::from_packed_bytes(&packed_bytes)
2868 .expect("Test value should convert successfully");
2869 assert_eq!(original, deserialized);
2870
2871 let structs = vec![original.clone(), original];
2873 let packed_multiple = TestDataStruct::to_packed_slice(&structs);
2874 assert_eq!(packed_multiple.len(), 2 * TestDataStruct::PACKED_SIZE);
2875
2876 let deserialized_multiple = TestDataStruct::from_packed_slice(&packed_multiple)
2877 .expect("Test value should convert successfully");
2878 assert_eq!(structs, deserialized_multiple);
2879
2880 let short_bytes = vec![0u8; TestDataStruct::PACKED_SIZE - 1];
2882 assert!(TestDataStruct::from_packed_bytes(&short_bytes).is_none());
2883
2884 let invalid_multiple = vec![0u8; TestDataStruct::PACKED_SIZE + 1];
2885 assert!(TestDataStruct::from_packed_slice(&invalid_multiple).is_none());
2886 }
2887
2888 #[cfg(test)]
2889 mod property_tests {
2890 use super::*;
2891 use proptest::prelude::*;
2892
2893 prop_compose! {
2895 fn valid_i24()(value in I24Repr::MIN..=I24Repr::MAX) -> I24 {
2896 I24::try_from_i32(value).expect("Test value should convert successfully")
2897 }
2898 }
2899
2900 prop_compose! {
2902 fn i24_pair()(a in valid_i24(), b in valid_i24()) -> (I24, I24) {
2903 (a, b)
2904 }
2905 }
2906
2907 proptest! {
2908 #[test]
2909 fn prop_to_from_i32_roundtrip(value in I24Repr::MIN..=I24Repr::MAX) {
2910 let i24_val = I24::try_from_i32(value).expect("Value in range should convert successfully");
2911 prop_assert_eq!(i24_val.to_i32(), value);
2912 }
2913
2914 #[test]
2915 fn prop_wrapping_from_i32_in_range(value in I24Repr::MIN..=I24Repr::MAX) {
2916 let i24_val = I24::wrapping_from_i32(value);
2917 prop_assert_eq!(i24_val.to_i32(), value);
2918 }
2919
2920 #[test]
2921 fn prop_saturating_from_i32_in_range(value in I24Repr::MIN..=I24Repr::MAX) {
2922 let i24_val = I24::saturating_from_i32(value);
2923 prop_assert_eq!(i24_val.to_i32(), value);
2924 }
2925
2926 #[test]
2927 fn prop_saturating_from_i32_above_max(value in I24Repr::MAX+1..=i32::MAX) {
2928 let i24_val = I24::saturating_from_i32(value);
2929 prop_assert_eq!(i24_val, I24::MAX);
2930 }
2931
2932 #[test]
2933 fn prop_saturating_from_i32_below_min(value in i32::MIN..=I24Repr::MIN-1) {
2934 let i24_val = I24::saturating_from_i32(value);
2935 prop_assert_eq!(i24_val, I24::MIN);
2936 }
2937
2938 #[test]
2939 fn prop_addition_commutative((a, b) in i24_pair()) {
2940 prop_assert_eq!(a + b, b + a);
2941 }
2942
2943 #[test]
2944 fn prop_addition_associative(a in valid_i24(), b in valid_i24(), c in valid_i24()) {
2945 prop_assert_eq!((a + b) + c, a + (b + c));
2946 }
2947
2948 #[test]
2949 fn prop_addition_identity(a in valid_i24()) {
2950 prop_assert_eq!(a + I24::zero(), a);
2951 prop_assert_eq!(I24::zero() + a, a);
2952 }
2953
2954 #[test]
2955 fn prop_subtraction_identity(a in valid_i24()) {
2956 prop_assert_eq!(a - I24::zero(), a);
2957 }
2958
2959 #[test]
2960 fn prop_subtraction_inverse(a in valid_i24()) {
2961 prop_assert_eq!(a - a, I24::zero());
2962 }
2963
2964 #[test]
2965 fn prop_multiplication_commutative((a, b) in i24_pair()) {
2966 prop_assert_eq!(a * b, b * a);
2967 }
2968
2969 #[test]
2970 fn prop_multiplication_identity(a in valid_i24()) {
2971 prop_assert_eq!(a * I24::one(), a);
2972 prop_assert_eq!(I24::one() * a, a);
2973 }
2974
2975 #[test]
2976 fn prop_multiplication_zero(a in valid_i24()) {
2977 prop_assert_eq!(a * I24::zero(), I24::zero());
2978 prop_assert_eq!(I24::zero() * a, I24::zero());
2979 }
2980
2981 #[test]
2982 fn prop_bitwise_and_commutative((a, b) in i24_pair()) {
2983 prop_assert_eq!(a & b, b & a);
2984 }
2985
2986 #[test]
2987 fn prop_bitwise_or_commutative((a, b) in i24_pair()) {
2988 prop_assert_eq!(a | b, b | a);
2989 }
2990
2991 #[test]
2992 fn prop_bitwise_xor_commutative((a, b) in i24_pair()) {
2993 prop_assert_eq!(a ^ b, b ^ a);
2994 }
2995
2996 #[test]
2997 fn prop_bitwise_xor_self_zero(a in valid_i24()) {
2998 prop_assert_eq!(a ^ a, I24::zero());
2999 }
3000
3001 #[test]
3002 fn prop_negation_involution(a in valid_i24()) {
3003 if a != I24::MIN {
3005 prop_assert_eq!(-(-a), a);
3006 }
3007 }
3008
3009 #[test]
3010 fn prop_byte_roundtrip_le(a in valid_i24()) {
3011 let bytes = a.to_le_bytes();
3012 let reconstructed = I24::from_le_bytes(bytes);
3013 prop_assert_eq!(a, reconstructed);
3014 }
3015
3016 #[test]
3017 fn prop_byte_roundtrip_be(a in valid_i24()) {
3018 let bytes = a.to_be_bytes();
3019 let reconstructed = I24::from_be_bytes(bytes);
3020 prop_assert_eq!(a, reconstructed);
3021 }
3022
3023 #[test]
3024 fn prop_byte_roundtrip_ne(a in valid_i24()) {
3025 let bytes = a.to_ne_bytes();
3026 let reconstructed = I24::from_ne_bytes(bytes);
3027 prop_assert_eq!(a, reconstructed);
3028 }
3029
3030 #[test]
3031 fn prop_checked_arithmetic_consistency(
3032 (a, b) in i24_pair()
3033 ) {
3034 let a_i32 = a.to_i32();
3036 let b_i32 = b.to_i32();
3037
3038 if let Some(expected_add) = a_i32.checked_add(b_i32) {
3039 if (I24Repr::MIN..=I24Repr::MAX).contains(&expected_add) {
3040 prop_assert_eq!(a.checked_add(b), Some(I24::try_from_i32(expected_add).expect("Test value should convert successfully")));
3041 }
3042 } else {
3043 prop_assert_eq!(a.checked_add(b), None);
3044 }
3045
3046 if let Some(expected_sub) = a_i32.checked_sub(b_i32) {
3047 if (I24Repr::MIN..=I24Repr::MAX).contains(&expected_sub) {
3048 prop_assert_eq!(a.checked_sub(b), Some(I24::try_from_i32(expected_sub).expect("Test value should convert successfully")));
3049 }
3050 } else {
3051 prop_assert_eq!(a.checked_sub(b), None);
3052 }
3053
3054 if let Some(expected_mul) = a_i32.checked_mul(b_i32) {
3055 if (I24Repr::MIN..=I24Repr::MAX).contains(&expected_mul) {
3056 prop_assert_eq!(a.checked_mul(b), Some(I24::try_from_i32(expected_mul).expect("Test value should convert successfully")));
3057 }
3058 } else {
3059 prop_assert_eq!(a.checked_mul(b), None);
3060 }
3061 }
3062
3063 #[test]
3064 fn prop_from_str_parse_display_roundtrip(a in valid_i24()) {
3065 let s = format!("{}", a);
3066 let parsed = I24::from_str(&s).expect("Test value should convert successfully");
3067 prop_assert_eq!(a, parsed);
3068 }
3069
3070 #[test]
3071 fn prop_try_from_u8_always_succeeds(value in any::<u8>()) {
3072 let result = <I24 as From<u8>>::from(value);
3073 prop_assert_eq!(result.to_i32(), value as i32);
3074 }
3075
3076 #[test]
3077 fn prop_try_from_i8_always_succeeds(value in any::<i8>()) {
3078 let result = <I24 as From<i8>>::from(value);
3079 prop_assert_eq!(result.to_i32(), value as i32);
3080 }
3081
3082 #[test]
3083 fn prop_try_from_u16_always_succeeds(value in any::<u16>()) {
3084 let result = <I24 as From<u16>>::from(value);
3085 prop_assert_eq!(result.to_i32(), value as i32);
3086 }
3087
3088 #[test]
3089 fn prop_try_from_i16_always_succeeds(value in any::<i16>()) {
3090 let result = <I24 as From<i16>>::from(value);
3091 prop_assert_eq!(result.to_i32(), value as i32);
3092 }
3093
3094 #[test]
3095 fn prop_try_from_u32_range_check(value in any::<u32>()) {
3096 let result = I24::try_from(value);
3097 if value <= I24Repr::MAX as u32 {
3098 prop_assert!(result.is_ok());
3099 prop_assert_eq!(result.expect("Test value should convert successfully").to_i32(), value as i32);
3100 } else {
3101 prop_assert!(result.is_err());
3102 }
3103 }
3104
3105 #[test]
3106 fn prop_try_from_i32_range_check(value in any::<i32>()) {
3107 let result = I24::try_from(value);
3108 if (I24Repr::MIN..=I24Repr::MAX).contains(&value) {
3109 prop_assert!(result.is_ok());
3110 prop_assert_eq!(result.expect("Test value should convert successfully").to_i32(), value);
3111 } else {
3112 prop_assert!(result.is_err());
3113 }
3114 }
3115
3116 #[test]
3117 fn prop_shift_operations(a in valid_i24(), shift in 0u32..8u32) {
3118 let shifted_left = a << shift;
3120 let shifted_right = a >> shift;
3121
3122 if shift == 0 {
3124 prop_assert_eq!(shifted_left, a);
3125 prop_assert_eq!(shifted_right, a);
3126 }
3127
3128 if a.to_i32() >= 0 && a.to_i32() <= (I24Repr::MAX >> shift) {
3131 let back_shifted = shifted_left >> shift;
3132 prop_assert_eq!(back_shifted, a);
3133 }
3134 }
3135
3136 #[cfg(feature = "alloc")]
3137 #[test]
3138 fn prop_bulk_serialization_roundtrip(values in proptest::collection::vec(valid_i24(), 0..100)) {
3139 let be_bytes = I24::write_i24s_be(&values);
3141 let be_reconstructed = I24::read_i24s_be(&be_bytes).expect("Test value should convert successfully");
3142 prop_assert_eq!(&values, &be_reconstructed);
3143
3144 let le_bytes = I24::write_i24s_le(&values);
3146 let le_reconstructed = I24::read_i24s_le(&le_bytes).expect("Test value should convert successfully");
3147 prop_assert_eq!(&values, &le_reconstructed);
3148
3149 let ne_bytes = I24::write_i24s_ne(&values);
3151 let ne_reconstructed = I24::read_i24s_ne(&ne_bytes).expect("Test value should convert successfully");
3152 prop_assert_eq!(&values, &ne_reconstructed);
3153 }
3154
3155 #[test]
3156 fn prop_ordering_consistency(a in valid_i24(), b in valid_i24()) {
3157 let a_i32 = a.to_i32();
3158 let b_i32 = b.to_i32();
3159
3160 prop_assert_eq!(a.cmp(&b), a_i32.cmp(&b_i32));
3161 prop_assert_eq!(a < b, a_i32 < b_i32);
3162 prop_assert_eq!(a <= b, a_i32 <= b_i32);
3163 prop_assert_eq!(a > b, a_i32 > b_i32);
3164 prop_assert_eq!(a >= b, a_i32 >= b_i32);
3165 prop_assert_eq!(a == b, a_i32 == b_i32);
3166 prop_assert_eq!(a != b, a_i32 != b_i32);
3167 }
3168
3169 #[test]
3170 fn prop_from_str_error_handling(s in "\\PC*") {
3171 match I24::from_str(&s) {
3173 Ok(val) => {
3174 prop_assert!(val.to_i32() >= I24Repr::MIN);
3176 prop_assert!(val.to_i32() <= I24Repr::MAX);
3177 }
3178 Err(e) => {
3179 let _ = e.kind();
3182 }
3183 }
3184 }
3185 }
3186 }
3187
3188 #[test]
3189 fn test_shift_operations_with_large_counts() {
3190 let a = i24!(0b1);
3192
3193 assert_eq!(a << 0, i24!(0b1));
3195 assert_eq!(a << 1, i24!(0b10));
3196 assert_eq!(a << 23, I24::MIN); assert_eq!(a << 24, i24!(0b1)); assert_eq!(a << 25, i24!(0b10)); assert_eq!(a << 47, I24::MIN); assert_eq!(a << 48, i24!(0b1)); let b = i24!(-1); assert_eq!(b >> 0, i24!(-1));
3205 assert_eq!(b >> 1, i24!(-1)); assert_eq!(b >> 23, i24!(-1)); assert_eq!(b >> 24, i24!(-1)); assert_eq!(b >> 25, i24!(-1)); assert_eq!(b >> 47, i24!(-1)); assert_eq!(b >> 48, i24!(-1)); let c = i24!(0x7FFFFF); assert_eq!(c >> 24, c); assert_eq!(c >> 25, c >> 1); }
3217
3218 #[test]
3219 fn test_ordering_across_boundaries() {
3220 assert!(i24!(-1) < i24!(0));
3222 assert!(i24!(0) < i24!(1));
3223 assert!(i24!(-1) < i24!(1));
3224
3225 assert!(I24::MIN < i24!(-1));
3227 assert!(i24!(-1) < i24!(0));
3228 assert!(i24!(0) < i24!(1));
3229 assert!(i24!(1) < I24::MAX);
3230 assert!(I24::MIN < I24::MAX);
3231
3232 for a in [-1000, -1, 0, 1, 1000] {
3234 for b in [-1000, -1, 0, 1, 1000] {
3235 let i24_a = I24::try_from_i32(a).expect("Test value should convert successfully");
3236 let i24_b = I24::try_from_i32(b).expect("Test value should convert successfully");
3237
3238 assert_eq!(
3239 i24_a < i24_b,
3240 a < b,
3241 "Ordering mismatch: I24({}) < I24({}) should be {}, but got {}",
3242 a,
3243 b,
3244 a < b,
3245 i24_a < i24_b
3246 );
3247 assert_eq!(
3248 i24_a == i24_b,
3249 a == b,
3250 "Equality mismatch: I24({}) == I24({}) should be {}, but got {}",
3251 a,
3252 b,
3253 a == b,
3254 i24_a == i24_b
3255 );
3256 }
3257 }
3258 }
3259
3260 #[test]
3261 fn test_convenience_methods() {
3262 assert_eq!(i24!(10).abs(), i24!(10));
3264 assert_eq!(i24!(-10).abs(), i24!(10));
3265 assert_eq!(I24::MIN.abs(), I24::MIN); assert_eq!(i24!(10).signum(), i24!(1));
3269 assert_eq!(i24!(0).signum(), i24!(0));
3270 assert_eq!(i24!(-10).signum(), i24!(-1));
3271
3272 assert!(!i24!(10).is_negative());
3274 assert!(!i24!(0).is_negative());
3275 assert!(i24!(-10).is_negative());
3276
3277 assert!(i24!(10).is_positive());
3278 assert!(!i24!(0).is_positive());
3279 assert!(!i24!(-10).is_positive());
3280
3281 assert_eq!(i24!(-3).clamp(i24!(-2), i24!(1)), i24!(-2));
3283 assert_eq!(i24!(0).clamp(i24!(-2), i24!(1)), i24!(0));
3284 assert_eq!(i24!(2).clamp(i24!(-2), i24!(1)), i24!(1));
3285
3286 assert_eq!(i24!(1).min(i24!(2)), i24!(1));
3288 assert_eq!(i24!(2).max(i24!(1)), i24!(2));
3289 }
3290
3291 #[test]
3292 fn test_signed_trait() {
3293 use num_traits::Signed;
3294
3295 let a = i24!(10);
3297 let b = i24!(5);
3298 let c = i24!(-3);
3299
3300 assert_eq!(a.abs_sub(&b), i24!(5)); assert_eq!(b.abs_sub(&a), i24!(0)); assert_eq!(a.abs_sub(&a), i24!(0)); assert_eq!(a.abs_sub(&c), i24!(13)); assert_eq!(c.abs_sub(&a), i24!(0)); assert_eq!(Signed::abs(&i24!(10)), i24!(10));
3313 assert_eq!(Signed::abs(&i24!(-10)), i24!(10));
3314 assert_eq!(Signed::signum(&i24!(10)), i24!(1));
3315 assert_eq!(Signed::signum(&i24!(-10)), i24!(-1));
3316 assert_eq!(Signed::signum(&i24!(0)), i24!(0));
3317 assert!(Signed::is_positive(&i24!(10)));
3318 assert!(!Signed::is_positive(&i24!(-10)));
3319 assert!(!Signed::is_positive(&i24!(0)));
3320 assert!(!Signed::is_negative(&i24!(10)));
3321 assert!(Signed::is_negative(&i24!(-10)));
3322 assert!(!Signed::is_negative(&i24!(0)));
3323
3324 assert_eq!(I24::MAX.abs_sub(&I24::MIN), I24::MAX - I24::MIN);
3326 assert_eq!(I24::MIN.abs_sub(&I24::MAX), i24!(0));
3327 }
3328
3329 #[test]
3330 fn test_wrapping_methods() {
3331 let a = i24!(100);
3333 let b = i24!(27);
3334
3335 assert_eq!(a.wrapping_add(b), a + b);
3336 assert_eq!(a.wrapping_sub(b), a - b);
3337 assert_eq!(a.wrapping_mul(b), a * b);
3338 assert_eq!(a.wrapping_div(b), a / b);
3339 assert_eq!(a.wrapping_rem(b), a % b);
3340 assert_eq!(a.wrapping_neg(), -a);
3341
3342 assert_eq!(I24::MAX.wrapping_add(i24!(1)), I24::MIN);
3344 assert_eq!(I24::MIN.wrapping_sub(i24!(1)), I24::MAX);
3345 assert_eq!(I24::MIN.wrapping_neg(), I24::MIN);
3346 }
3347
3348 #[test]
3349 fn test_saturating_methods() {
3350 let a = i24!(100);
3352 let b = i24!(27);
3353
3354 assert_eq!(a.saturating_add(b), i24!(127));
3355 assert_eq!(a.saturating_sub(b), i24!(73));
3356 assert_eq!(a.saturating_mul(b), i24!(2700));
3357
3358 assert_eq!(I24::MAX.saturating_add(i24!(1)), I24::MAX);
3360 assert_eq!(I24::MIN.saturating_sub(i24!(1)), I24::MIN);
3361 assert_eq!(I24::MIN.saturating_neg(), I24::MAX);
3362 assert_eq!(I24::MIN.saturating_div(i24!(-1)), I24::MAX);
3363 }
3364
3365 #[cfg(feature = "num-cast")]
3366 #[test]
3367 fn test_num_cast_trait() {
3368 use num_traits::NumCast;
3369
3370 assert_eq!(
3372 <I24 as NumCast>::from(1000i32),
3373 Some(I24::try_from_i32(1000).unwrap())
3374 );
3375 assert_eq!(
3376 <I24 as NumCast>::from(500u16),
3377 Some(I24::try_from_i32(500).unwrap())
3378 );
3379 assert_eq!(
3380 <I24 as NumCast>::from(100i8),
3381 Some(I24::try_from_i32(100).unwrap())
3382 );
3383 assert_eq!(
3384 <I24 as NumCast>::from(200u8),
3385 Some(I24::try_from_i32(200).unwrap())
3386 );
3387 assert_eq!(
3388 <I24 as NumCast>::from(-1000i32),
3389 Some(I24::try_from_i32(-1000).unwrap())
3390 );
3391
3392 assert_eq!(<I24 as NumCast>::from(10_000_000i32), None);
3394 assert_eq!(<I24 as NumCast>::from(-10_000_000i32), None);
3395 assert_eq!(<I24 as NumCast>::from(20_000_000u32), None);
3396
3397 assert_eq!(<I24 as NumCast>::from(I24::MAX.to_i32()), Some(I24::MAX));
3399 assert_eq!(<I24 as NumCast>::from(I24::MIN.to_i32()), Some(I24::MIN));
3400
3401 assert_eq!(
3403 <I24 as NumCast>::from(1000.0f32),
3404 Some(I24::try_from_i32(1000).unwrap())
3405 );
3406 assert_eq!(
3407 <I24 as NumCast>::from(-500.5f32),
3408 Some(I24::try_from_i32(-500).unwrap())
3409 );
3410 assert_eq!(<I24 as NumCast>::from(1e10f64), None); }
3412}
3413#[cfg(test)]
3414mod wire_tests {
3415 use super::*;
3416
3417 #[test]
3418 fn test_i24bytes_size_and_alignment() {
3419 assert_eq!(core::mem::size_of::<I24Bytes>(), 3);
3420 assert_eq!(core::mem::align_of::<I24Bytes>(), 1);
3421 }
3422
3423 #[test]
3424 fn test_i24bytes_round_trip_le() {
3425 let test_values = [
3426 0i32, 1, -1, 123456, -123456, 8388607, -8388608, 255, -255, 65536, -65536,
3429 ];
3430
3431 for &value in &test_values {
3432 let i24_val = I24::try_from(value).expect("Test value should convert successfully");
3433 let wire = I24Bytes::from_i24_le(i24_val);
3434 let recovered = wire.to_i24_le();
3435 assert_eq!(i24_val, recovered, "Round-trip failed for value: {}", value);
3436 }
3437 }
3438
3439 #[test]
3440 fn test_i24bytes_round_trip_be() {
3441 let test_values = [
3442 0i32, 1, -1, 123456, -123456, 8388607, -8388608, ];
3445
3446 for &value in &test_values {
3447 let i24_val = I24::try_from(value).expect("Test value should convert successfully");
3448 let wire = I24Bytes::from_i24_be(i24_val);
3449 let recovered = wire.to_i24_be();
3450 assert_eq!(i24_val, recovered, "Round-trip failed for value: {}", value);
3451 }
3452 }
3453
3454 #[test]
3455 fn test_i24bytes_endianness_difference() {
3456 let value = I24::try_from(0x123456).expect("Test value should convert successfully");
3457 let le_bytes = I24Bytes::from_i24_le(value);
3458 let be_bytes = I24Bytes::from_i24_be(value);
3459
3460 assert_ne!(le_bytes.to_bytes(), be_bytes.to_bytes());
3462
3463 assert_eq!(le_bytes.to_i24_le(), be_bytes.to_i24_be());
3465 }
3466
3467 #[test]
3468 fn test_i24bytes_specific_byte_patterns() {
3469 let le_bytes = I24Bytes([0x40, 0xE2, 0x01]); let be_bytes = I24Bytes([0x01, 0xE2, 0x40]); assert_eq!(
3474 le_bytes.to_i24_le(),
3475 I24::try_from(123456).expect("Test value should convert successfully")
3476 );
3477 assert_eq!(
3478 be_bytes.to_i24_be(),
3479 I24::try_from(123456).expect("Test value should convert successfully")
3480 );
3481 }
3482
3483 #[test]
3484 fn test_i24bytes_from_raw_bytes() {
3485 let raw_bytes = [0x12, 0x34, 0x56];
3486 let wire = I24Bytes::from_bytes(raw_bytes);
3487 assert_eq!(wire.to_bytes(), raw_bytes);
3488 }
3489
3490 #[test]
3491 fn test_i24bytes_bytemuck_traits() {
3492 assert_eq!(core::mem::size_of::<I24Bytes>(), 3);
3494 assert_eq!(core::mem::align_of::<I24Bytes>(), 1);
3495
3496 let bytes = [0x12, 0x34, 0x56, 0xAB, 0xCD, 0xEF];
3498
3499 let first_i24 = I24Bytes([0x12, 0x34, 0x56]);
3501
3502 let first_array = [first_i24];
3504 let first_bytes: &[u8] = bytemuck::cast_slice(&first_array);
3505 assert_eq!(first_bytes, &[0x12, 0x34, 0x56]);
3506
3507 let wire_slice = bytemuck::try_cast_slice::<u8, I24Bytes>(&bytes)
3509 .expect("Cast should succeed for properly aligned bytes");
3510 assert_eq!(wire_slice.len(), 2);
3511 assert_eq!(wire_slice[0].to_bytes(), [0x12, 0x34, 0x56]);
3512 assert_eq!(wire_slice[1].to_bytes(), [0xAB, 0xCD, 0xEF]);
3513 }
3514
3515 #[test]
3516 fn test_i24bytes_in_simple_struct() {
3517 use bytemuck::{Pod, Zeroable};
3518
3519 #[repr(C)]
3521 #[derive(Copy, Clone, Debug, Pod, Zeroable)]
3522 struct SimpleWire {
3523 value: I24Bytes,
3524 padding: [u8; 1], }
3526
3527 let wire = SimpleWire {
3528 value: I24Bytes::from_i24_le(
3529 I24::try_from(123456).expect("Test value should convert successfully"),
3530 ),
3531 padding: [0xFF],
3532 };
3533
3534 let bytes: [u8; 4] = bytemuck::cast(wire);
3536 let reconstructed: SimpleWire = bytemuck::cast(bytes);
3537
3538 assert_eq!(
3539 reconstructed.value.to_i24_le(),
3540 I24::try_from(123456).expect("Test value should convert successfully")
3541 );
3542 assert_eq!(reconstructed.padding, [0xFF]);
3543 }
3544
3545 #[test]
3546 fn test_i24bytes_sign_extension() {
3547 let negative_value = I24::try_from(-1).expect("Test value should convert successfully");
3549 let wire_le = I24Bytes::from_i24_le(negative_value);
3550 let wire_be = I24Bytes::from_i24_be(negative_value);
3551
3552 let expected_bytes = [0xFF, 0xFF, 0xFF];
3554 assert_eq!(wire_le.to_bytes(), expected_bytes);
3555 assert_eq!(wire_be.to_bytes(), expected_bytes);
3556
3557 assert_eq!(wire_le.to_i24_le(), negative_value);
3559 assert_eq!(wire_be.to_i24_be(), negative_value);
3560 }
3561
3562 #[test]
3563 fn test_i24bytes_zero_initialization() {
3564 let zero_wire: I24Bytes = bytemuck::Zeroable::zeroed();
3565 assert_eq!(zero_wire.to_bytes(), [0, 0, 0]);
3566 assert_eq!(
3567 zero_wire.to_i24_le(),
3568 I24::try_from(0).expect("Test value should convert successfully")
3569 );
3570 assert_eq!(
3571 zero_wire.to_i24_be(),
3572 I24::try_from(0).expect("Test value should convert successfully")
3573 );
3574 }
3575
3576 #[cfg(feature = "zerocopy")]
3577 #[test]
3578 fn test_i24bytes_zerocopy_traits() {
3579 let bytes = [0x12, 0x34, 0x56];
3581
3582 let wire = I24Bytes::from_bytes(bytes);
3584 assert_eq!(wire.to_bytes(), bytes);
3585
3586 assert_eq!(core::mem::align_of::<I24Bytes>(), 1);
3588
3589 }
3592
3593 #[cfg(feature = "ndarray")]
3594 #[test]
3595 fn test_ndarray_scalar_operand() {
3596 use ndarray::ScalarOperand;
3599
3600 let i24_val = crate::i24!(100);
3601 let i24_bytes = I24Bytes([0x64, 0x00, 0x00]); fn check_scalar_operand<T: ScalarOperand>(_: T) {}
3605 check_scalar_operand(i24_val);
3606 check_scalar_operand(i24_bytes);
3607
3608 }
3611}