Skip to main content

ttf_view/types/
uint24mod.rs

1use crate::util::impl_fmt_with;
2
3/// The 24-bit unsigned integer type.
4///
5/// **Important note:** Despite only representing a valid range of a 24-bit unsigned integers,
6/// this type is actually backed by [`u32`] to ensure it's aligned well in the registers for
7/// efficient math, copies and pretty much all operations in general.
8///
9/// ```
10/// use ttf_view::types::{BigEndian, u24, uint24};
11///
12/// // u24 is backed by u32
13/// assert_eq!(size_of::<u24>(), 4);
14///
15/// // big-endian types still have correct size (backed by [u8; 3])
16/// assert_eq!(size_of::<uint24>(), 3);
17/// assert_eq!(size_of::<BigEndian<u24>>(), 3);
18/// ```
19#[derive(Copy, Hash)]
20#[derive_const(Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
21#[repr(transparent)]
22pub struct u24(u32);
23
24impl u24 {
25    /// The size of this integer type in bits.
26    ///
27    /// # Examples
28    ///
29    /// ```
30    /// # use ttf_view::types::u24;
31    /// assert_eq!(u24::BITS, 24);
32    /// ```
33    pub const BITS: u32 = 24;
34    /// The smallest value that can be represented by this integer type.
35    ///
36    /// # Examples
37    ///
38    /// ```
39    /// # use ttf_view::types::u24;
40    /// assert_eq!(u24::MIN, 0);
41    /// ```
42    pub const MIN: Self = Self(0x000000);
43    /// The largest value that can be represented by this integer type (2<sup>24</sup> &minus; 1).
44    ///
45    /// # Examples
46    ///
47    /// ```
48    /// # use ttf_view::types::u24;
49    /// assert_eq!(u24::MAX, 16_777_215);
50    /// ```
51    pub const MAX: Self = Self(0xFFFFFF);
52
53    /// Creates a [`u24`] from [`u32`].
54    ///
55    /// # Examples
56    ///
57    /// ```
58    /// use ttf_view::types::u24;
59    ///
60    /// assert_eq!(u24::new(1234).unwrap(), 1234);
61    /// assert_eq!(u24::new(15_000_000).unwrap(), 15_000_000);
62    /// assert_eq!(u24::new(0xFFFFFF).unwrap(), 0xFFFFFF);
63    /// assert_eq!(u24::new(17_000_000), None);
64    /// assert_eq!(u24::new(3_999_000_000), None);
65    /// ```
66    pub const fn new(num: u32) -> Option<Self> {
67        if num <= Self::MAX.0 { Some(Self(num)) } else { None }
68    }
69    /// Creates a [`u24`] from [`u32`], saturating at the numeric bounds.
70    ///
71    /// # Examples
72    ///
73    /// ```
74    /// use ttf_view::types::u24;
75    ///
76    /// assert_eq!(u24::new_saturating(1234), 1234);
77    /// assert_eq!(u24::new_saturating(15_000_000), 15_000_000);
78    /// assert_eq!(u24::new_saturating(0xFFFFFF), 0xFFFFFF);
79    /// assert_eq!(u24::new_saturating(17_000_000), 0xFFFFFF);
80    /// assert_eq!(u24::new_saturating(3_999_000_000), 0xFFFFFF);
81    /// ```
82    pub const fn new_saturating(num: u32) -> Self {
83        Self(num.min(Self::MAX.0))
84    }
85    /// Creates a [`u24`] from [`u32`], truncating the most significant 8 bits.
86    ///
87    /// # Examples
88    ///
89    /// ```
90    /// use ttf_view::types::u24;
91    ///
92    /// assert_eq!(u24::new_truncating(1234), 1234);
93    /// assert_eq!(u24::new_truncating(15_000_000), 15_000_000);
94    /// assert_eq!(u24::new_truncating(0xFFFFFF), 0xFFFFFF);
95    /// assert_eq!(u24::new_truncating(17_000_000), 222784);
96    /// assert_eq!(u24::new_truncating(3_999_000_000), 6022592);
97    /// ```
98    pub const fn new_truncating(num: u32) -> Self {
99        Self(num & Self::MAX.0)
100    }
101
102    /// Creates a [`u24`] from big-endian bytes.
103    ///
104    /// # Examples
105    ///
106    /// ```
107    /// use ttf_view::types::u24;
108    ///
109    /// assert_eq!(u24::from_be_bytes([0x00, 0x00, 0x07]), 7);
110    /// assert_eq!(u24::from_be_bytes([0x12, 0x34, 0x56]), 0x00123456);
111    /// ```
112    pub const fn from_be_bytes(bytes: [u8; 3]) -> Self {
113        let [a, b, c] = bytes;
114        Self(u32::from_be_bytes([0, a, b, c]))
115    }
116    /// Gets this [`u24`]'s big-endian bytes.
117    ///
118    /// # Examples
119    ///
120    /// ```
121    /// use ttf_view::types::u24;
122    ///
123    /// assert_eq!(u24::new(7).unwrap().to_be_bytes(), [0x00, 0x00, 0x07]);
124    /// assert_eq!(u24::new(0x00123456).unwrap().to_be_bytes(), [0x12, 0x34, 0x56]);
125    /// ```
126    pub const fn to_be_bytes(self) -> [u8; 3] {
127        *self.0.to_be_bytes().last_chunk().unwrap()
128    }
129
130    /// Gets the value of this [`u24`] as [`u32`].
131    ///
132    /// # Examples
133    ///
134    /// ```
135    /// use ttf_view::types::u24;
136    ///
137    /// assert_eq!(u24::new(1234).unwrap(), 1234);
138    /// assert_eq!(u24::new(15_000_000).unwrap(), 15_000_000);
139    /// ```
140    pub const fn get(self) -> u32 {
141        self.0
142    }
143
144    pub const fn wrapping_neg(self) -> Self {
145        Self(self.0.wrapping_neg() & Self::MAX.0)
146    }
147    pub const fn wrapping_add(self, rhs: Self) -> Self {
148        Self(self.0.wrapping_add(rhs.0) & Self::MAX.0)
149    }
150    pub const fn wrapping_sub(self, rhs: Self) -> Self {
151        Self(self.0.wrapping_sub(rhs.0) & Self::MAX.0)
152    }
153    pub const fn wrapping_mul(self, rhs: Self) -> Self {
154        Self(self.0.wrapping_mul(rhs.0) & Self::MAX.0)
155    }
156    /// # Panics
157    ///
158    /// This function will panic if `rhs == 0`.
159    pub const fn wrapping_div(self, rhs: Self) -> Self {
160        Self(self.0.wrapping_div(rhs.0))
161    }
162    /// # Panics
163    ///
164    /// This function will panic if `rhs == 0`.
165    pub const fn wrapping_rem(self, rhs: Self) -> Self {
166        Self(self.0.wrapping_rem(rhs.0))
167    }
168
169    pub const fn saturating_add(self, rhs: Self) -> Self {
170        Self(self.0.wrapping_add(rhs.0).min(Self::MAX.0))
171    }
172    pub const fn saturating_sub(self, rhs: Self) -> Self {
173        Self(self.0.saturating_sub(rhs.0))
174    }
175    pub const fn saturating_mul(self, rhs: Self) -> Self {
176        Self(self.0.widening_mul(rhs.0).min(Self::MAX.0 as u64) as u32)
177    }
178    /// # Panics
179    ///
180    /// This function will panic if `rhs == 0`.
181    pub const fn saturating_div(self, rhs: Self) -> Self {
182        Self(self.0.saturating_div(rhs.0))
183    }
184
185    pub const fn checked_neg(self) -> Option<Self> {
186        self.0.checked_neg().map(Self)
187    }
188    pub const fn checked_add(self, rhs: Self) -> Option<Self> {
189        self.0.wrapping_add(rhs.0).try_into().ok()
190    }
191    pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
192        self.0.checked_sub(rhs.0).map(Self)
193    }
194    pub const fn checked_mul(self, rhs: Self) -> Option<Self> {
195        self.0.widening_mul(rhs.0).try_into().ok()
196    }
197    pub const fn checked_div(self, rhs: Self) -> Option<Self> {
198        self.0.checked_div(rhs.0).map(Self)
199    }
200    pub const fn checked_rem(self, rhs: Self) -> Option<Self> {
201        self.0.checked_rem(rhs.0).map(Self)
202    }
203}
204
205/// Performs addition `+` (panics on overflow in debug configuration).
206const impl std::ops::Add for u24 {
207    type Output = Self;
208    fn add(self, rhs: Self) -> Self::Output {
209        #[cfg(debug_assertions)]
210        return self.checked_add(rhs).expect("attempt to add with overflow");
211        #[cfg(not(debug_assertions))]
212        return self.wrapping_add(rhs);
213    }
214}
215/// Performs subtraction `-` (panics on overflow in debug configuration).
216const impl std::ops::Sub for u24 {
217    type Output = Self;
218    fn sub(self, rhs: Self) -> Self::Output {
219        #[cfg(debug_assertions)]
220        return self.checked_sub(rhs).expect("attempt to subtract with overflow");
221        #[cfg(not(debug_assertions))]
222        return self.wrapping_sub(rhs);
223    }
224}
225/// Performs multiplication `*` (panics on overflow in debug configuration).
226const impl std::ops::Mul for u24 {
227    type Output = Self;
228    fn mul(self, rhs: Self) -> Self::Output {
229        #[cfg(debug_assertions)]
230        return self.checked_mul(rhs).expect("attempt to multiply with overflow");
231        #[cfg(not(debug_assertions))]
232        return self.wrapping_mul(rhs);
233    }
234}
235/// Performs division `/`.
236///
237/// # Panics
238///
239/// This operation will panic if `rhs == 0`.
240const impl std::ops::Div for u24 {
241    type Output = Self;
242    fn div(self, rhs: Self) -> Self::Output {
243        #[cfg(debug_assertions)]
244        return self.checked_div(rhs).expect("attempt to divide by zero");
245        #[cfg(not(debug_assertions))]
246        return self.wrapping_div(rhs);
247    }
248}
249/// Performs remainder operation `%`.
250///
251/// # Panics
252///
253/// This operation will panic if `rhs == 0`.
254const impl std::ops::Rem for u24 {
255    type Output = Self;
256    fn rem(self, rhs: Self) -> Self::Output {
257        #[cfg(debug_assertions)]
258        return self
259            .checked_rem(rhs)
260            .expect("attempt to calculate the remainder with a divisor of zero");
261
262        #[cfg(not(debug_assertions))]
263        return self.wrapping_rem(rhs);
264    }
265}
266
267impl_fmt_with! {
268    Debug, Display, Binary, Octal, LowerHex, UpperHex, LowerExp, UpperExp:
269    |this: &u24| this.get()
270}
271
272const impl PartialEq<u32> for u24 {
273    fn eq(&self, other: &u32) -> bool {
274        self.get().eq(other)
275    }
276}
277const impl PartialOrd<u32> for u24 {
278    fn partial_cmp(&self, other: &u32) -> Option<std::cmp::Ordering> {
279        Some(self.get().cmp(other))
280    }
281}
282
283const impl std::str::FromStr for u24 {
284    type Err = ();
285    fn from_str(s: &str) -> Result<Self, Self::Err> {
286        u32::from_str(s).or(Err(())).and_then(Self::try_from)
287    }
288}
289
290const impl TryFrom<usize> for u24 {
291    type Error = ();
292    fn try_from(value: usize) -> Result<Self, Self::Error> {
293        value.try_into().ok().and_then(Self::new).ok_or(())
294    }
295}
296const impl TryFrom<u64> for u24 {
297    type Error = ();
298    fn try_from(value: u64) -> Result<Self, Self::Error> {
299        value.try_into().ok().and_then(Self::new).ok_or(())
300    }
301}
302const impl TryFrom<u32> for u24 {
303    type Error = ();
304    fn try_from(value: u32) -> Result<Self, Self::Error> {
305        Self::new(value).ok_or(())
306    }
307}
308const impl From<u16> for u24 {
309    fn from(value: u16) -> Self {
310        Self(value as u32)
311    }
312}
313const impl From<u8> for u24 {
314    fn from(value: u8) -> Self {
315        Self(value as u32)
316    }
317}
318
319const impl TryFrom<u24> for usize {
320    type Error = ();
321    fn try_from(value: u24) -> Result<Self, Self::Error> {
322        value.get().try_into().ok().ok_or(())
323    }
324}
325const impl From<u24> for u64 {
326    fn from(value: u24) -> Self {
327        value.get() as u64
328    }
329}
330const impl From<u24> for u32 {
331    fn from(value: u24) -> Self {
332        value.get()
333    }
334}
335const impl TryFrom<u24> for u16 {
336    type Error = ();
337    fn try_from(value: u24) -> Result<Self, Self::Error> {
338        value.get().try_into().ok().ok_or(())
339    }
340}
341const impl TryFrom<u24> for u8 {
342    type Error = ();
343    fn try_from(value: u24) -> Result<Self, Self::Error> {
344        value.get().try_into().ok().ok_or(())
345    }
346}