cuprate_helper/
cast.rs

1//! Casting.
2//!
3//! This modules provides utilities for casting between types.
4//!
5//! `#[no_std]` compatible.
6//!
7//! # 64-bit invariant
8//! This module is available on 32-bit arches although panics
9//! will occur between lossy casts, e.g. [`u64_to_usize`] where
10//! the input is larger than [`u32::MAX`].
11//!
12//! On 64-bit arches, all functions are lossless.
13
14// TODO:
15// These casting functions are heavily used throughout the codebase
16// yet it is not enforced that all usages are correct in 32-bit cases.
17// Panicking may be a short-term solution - find a better fix for 32-bit arches.
18
19#![allow(clippy::cast_possible_truncation)]
20
21#[rustfmt::skip]
22//============================ SAFETY: DO NOT REMOVE ===========================//
23//                                                                              //
24//                                                                              //
25//                   Only allow building {32,64}-bit targets.                   //
26//          This allows us to assume {32,64}-bit invariants in this file.       //
27    #[cfg(not(any(target_pointer_width = "64", target_pointer_width = "32")))]
28      compile_error!("This module is only compatible with {32,64}-bit CPUs");
29//                                                                              //
30//                                                                              //
31//============================ SAFETY: DO NOT REMOVE ===========================//
32
33#[cfg(target_pointer_width = "64")]
34mod functions {
35    /// Cast [`u64`] to [`usize`].
36    #[inline(always)]
37    pub const fn u64_to_usize(u: u64) -> usize {
38        u as usize
39    }
40
41    /// Cast [`i64`] to [`isize`].
42    #[inline(always)]
43    pub const fn i64_to_isize(i: i64) -> isize {
44        i as isize
45    }
46}
47
48#[cfg(target_pointer_width = "32")]
49mod functions {
50    /// Cast [`u64`] to [`usize`].
51    ///
52    /// # Panics
53    /// This panics on 32-bit arches if `u` is larger than [`u32::MAX`].
54    #[inline(always)]
55    pub const fn u64_to_usize(u: u64) -> usize {
56        if u > u32::MAX as u64 {
57            panic!()
58        } else {
59            u as usize
60        }
61    }
62
63    /// Cast [`i64`] to [`isize`].
64    ///
65    /// # Panics
66    /// This panics on 32-bit arches if `i` is lesser than [`i32::MIN`] or greater [`i32::MAX`].
67    #[inline(always)]
68    pub const fn i64_to_isize(i: i64) -> isize {
69        if i < i32::MIN as i64 || i > i32::MAX as i64 {
70            panic!()
71        } else {
72            i as isize
73        }
74    }
75}
76
77pub use functions::{i64_to_isize, u64_to_usize};
78
79/// Cast [`u32`] to [`usize`].
80#[inline(always)]
81pub const fn u32_to_usize(u: u32) -> usize {
82    u as usize
83}
84
85/// Cast [`i32`] to [`isize`].
86#[inline(always)]
87pub const fn i32_to_isize(i: i32) -> isize {
88    i as isize
89}
90
91/// Cast [`usize`] to [`u64`].
92#[inline(always)]
93pub const fn usize_to_u64(u: usize) -> u64 {
94    u as u64
95}
96
97/// Cast [`isize`] to [`i64`].
98#[inline(always)]
99pub const fn isize_to_i64(i: isize) -> i64 {
100    i as i64
101}
102
103//---------------------------------------------------------------------------------------------------- Tests
104#[cfg(test)]
105mod test {
106    use super::*;
107
108    #[test]
109    #[cfg(target_pointer_width = "64")]
110    fn max_64bit() {
111        assert_eq!(u32_to_usize(u32::MAX), usize::try_from(u32::MAX).unwrap());
112        assert_eq!(usize_to_u64(u32_to_usize(u32::MAX)), u64::from(u32::MAX));
113
114        assert_eq!(u64_to_usize(u64::MAX), usize::MAX);
115        assert_eq!(usize_to_u64(u64_to_usize(u64::MAX)), u64::MAX);
116
117        assert_eq!(usize_to_u64(usize::MAX), u64::MAX);
118        assert_eq!(u64_to_usize(usize_to_u64(usize::MAX)), usize::MAX);
119
120        assert_eq!(i32_to_isize(i32::MAX), isize::try_from(i32::MAX).unwrap());
121        assert_eq!(isize_to_i64(i32_to_isize(i32::MAX)), i64::from(i32::MAX));
122
123        assert_eq!(i64_to_isize(i64::MAX), isize::MAX);
124        assert_eq!(isize_to_i64(i64_to_isize(i64::MAX)), i64::MAX);
125
126        assert_eq!(isize_to_i64(isize::MAX), i64::MAX);
127        assert_eq!(i64_to_isize(isize_to_i64(isize::MAX)), isize::MAX);
128    }
129
130    #[test]
131    #[cfg(target_pointer_width = "32")]
132    #[should_panic]
133    fn panic_u64_32bit() {
134        u64_to_usize(u64::from(u32::MAX + 1));
135    }
136
137    #[test]
138    #[cfg(target_pointer_width = "32")]
139    #[should_panic]
140    fn panic_i64_lesser_32bit() {
141        i64_to_usize(i64::from(i32::MIN - 1));
142    }
143
144    #[test]
145    #[cfg(target_pointer_width = "32")]
146    #[should_panic]
147    fn panic_i64_greater_32bit() {
148        i64_to_usize(i64::from(i32::MAX + 1));
149    }
150}