1use std::convert::TryFrom;
20use std::num::TryFromIntError;
21
22#[derive(Clone, Copy, Debug, Eq, PartialEq)]
24pub struct U24(u32);
25
26impl From<u16> for U24 {
27 fn from(value: u16) -> Self {
28 Self(u32::from(value))
29 }
30}
31
32impl TryFrom<u32> for U24 {
33 type Error = ();
34
35 fn try_from(value: u32) -> Result<Self, Self::Error> {
36 if value >= (1 << 24) { Err(()) } else { Ok(Self(value)) }
37 }
38}
39
40impl TryFrom<usize> for U24 {
41 type Error = ();
42
43 fn try_from(value: usize) -> Result<Self, Self::Error> {
44 if value >= (1 << 24) { Err(()) } else { Ok(Self(value as u32)) }
45 }
46}
47
48impl From<U24> for u32 {
49 fn from(value: U24) -> Self {
50 value.0
51 }
52}
53
54impl TryFrom<U24> for usize {
55 type Error = TryFromIntError;
56
57 fn try_from(value: U24) -> Result<Self, Self::Error> {
58 Self::try_from(value.0)
59 }
60}
61
62impl U24 {
63 pub(crate) const MAX: U24 = U24(1 << 24);
65}
66
67trait UncheckedFrom {
69 type T;
71
72 fn unchecked_from(value: Self::T) -> Self;
77}
78
79macro_rules! impl_unchecked_cast {
84 ( $name:ident, $from_ty:ty, $to_ty:ty, primitive ) => {
85 pub(crate) fn $name(value: $from_ty) -> $to_ty {
86 if cfg!(debug_assertions) {
87 <$to_ty>::try_from(value).unwrap()
88 } else {
89 value as $to_ty
90 }
91 }
92 };
93
94 ( $name:ident, $from_ty:ty, $to_ty:ty, user_defined ) => {
95 pub(crate) fn $name(value: $from_ty) -> $to_ty {
96 if cfg!(debug_assertions) {
97 <$to_ty>::try_from(value).unwrap()
98 } else {
99 <$to_ty>::unchecked_from(value)
100 }
101 }
102 };
103}
104
105impl_unchecked_cast!(unchecked_u32_as_u8, u32, u8, primitive);
106impl_unchecked_cast!(unchecked_u32_as_u16, u32, u16, primitive);
107impl_unchecked_cast!(unchecked_u32_as_usize, u32, usize, primitive);
108impl_unchecked_cast!(unchecked_u64_as_u8, u64, u8, primitive);
109impl_unchecked_cast!(unchecked_usize_as_u8, usize, u8, primitive);
110
111impl UncheckedFrom for usize {
112 type T = U24;
113
114 fn unchecked_from(value: Self::T) -> Self {
115 value.0 as Self
116 }
117}
118
119impl_unchecked_cast!(unchecked_u24_as_usize, U24, usize, user_defined);
120
121#[cfg(test)]
122mod tests {
123 use super::*;
124
125 #[test]
126 fn test_unchecked_u32_as_u8() {
127 assert_eq!(10u8, unchecked_u32_as_u8(10u32));
128 }
129
130 #[test]
131 fn test_unchecked_u32_as_u16() {
132 assert_eq!(10u16, unchecked_u32_as_u16(10u32));
133 }
134
135 #[test]
136 fn test_unchecked_u32_as_usize() {
137 assert_eq!(10usize, unchecked_u32_as_usize(10u32));
138 }
139
140 #[test]
141 fn test_unchecked_u64_as_u8() {
142 assert_eq!(10u8, unchecked_u64_as_u8(10u64));
143 }
144
145 #[test]
146 fn test_unchecked_usize_as_u8() {
147 assert_eq!(10u8, unchecked_usize_as_u8(10_usize));
148 }
149
150 #[test]
151 fn test_unchecked_u24_as_usize() {
152 assert_eq!(10_usize, unchecked_u24_as_usize(U24(10)));
153 }
154}