pprof_util/
cast.rs

1// Copyright Materialize, Inc. and contributors. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License in the LICENSE file at the
6// root of this repository, or online at
7//
8//     http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16//! Cast utilities.
17
18use num::traits::bounds::UpperBounded;
19use num::Signed;
20use std::error::Error;
21use std::fmt;
22use std::ops::Deref;
23
24/// A trait for safe, simple, and infallible casts.
25///
26/// `CastFrom` is like [`std::convert::From`], but it is implemented for some
27/// platform-specific casts that are missing from the standard library. For
28/// example, there is no `From<u32> for usize` implementation, because Rust may
29/// someday support platforms where usize is smaller than 32 bits. Since we
30/// don't care about such platforms, we are happy to provide a `CastFrom<u32>
31/// for usize` implementation.
32///
33/// `CastFrom` should be preferred to the `as` operator, since the `as` operator
34/// will silently truncate if the target type is smaller than the source type.
35/// When applicable, `CastFrom` should also be preferred to the
36/// [`std::convert::TryFrom`] trait, as `TryFrom` will produce a runtime error,
37/// while `CastFrom` will produce a compile-time error.
38pub trait CastFrom<T> {
39    /// Performs the cast.
40    fn cast_from(from: T) -> Self;
41}
42
43macro_rules! cast_from {
44    ($from:ty, $to:ty) => {
45        paste::paste! {
46            impl crate::cast::CastFrom<$from> for $to {
47                #[allow(clippy::as_conversions)]
48                #[allow(unused)]
49                fn cast_from(from: $from) -> $to {
50                    from as $to
51                }
52            }
53
54            /// Casts [`$from`] to [`$to`].
55            ///
56            /// This is equivalent to the [`crate::cast::CastFrom`] implementation but is
57            /// available as a `const fn`.
58            #[allow(clippy::as_conversions)]
59            #[allow(unused)]
60            pub const fn [< $from _to_ $to >](from: $from) -> $to {
61                from as $to
62            }
63        }
64    };
65}
66
67#[cfg(target_pointer_width = "32")]
68/// Safe casts for 32bit platforms
69mod target32 {
70    // size_of<from> < size_of<target>
71    cast_from!(u8, usize);
72    cast_from!(u16, usize);
73    cast_from!(u8, isize);
74    cast_from!(i8, isize);
75    cast_from!(u16, isize);
76    cast_from!(i16, isize);
77
78    cast_from!(usize, u64);
79    cast_from!(usize, i64);
80    cast_from!(usize, u128);
81    cast_from!(usize, i128);
82    cast_from!(isize, i64);
83    cast_from!(isize, i128);
84
85    // size_of<from> == size_of<target>
86    cast_from!(usize, u32);
87    cast_from!(isize, i32);
88    cast_from!(u32, usize);
89    cast_from!(i32, isize);
90}
91
92#[cfg(target_pointer_width = "64")]
93/// Safe casts for 64bit platforms
94pub mod target64 {
95    // size_of<from> < size_of<target>
96    cast_from!(u8, usize);
97    cast_from!(u16, usize);
98    cast_from!(u32, usize);
99    cast_from!(u8, isize);
100    cast_from!(i8, isize);
101    cast_from!(u16, isize);
102    cast_from!(i16, isize);
103    cast_from!(u32, isize);
104    cast_from!(i32, isize);
105
106    cast_from!(usize, u128);
107    cast_from!(usize, i128);
108    cast_from!(isize, i128);
109
110    // size_of<from> == size_of<target>
111    cast_from!(usize, u64);
112    cast_from!(isize, i64);
113    cast_from!(u64, usize);
114    cast_from!(i64, isize);
115}
116
117// TODO(petrosagg): remove these once the std From impls become const
118cast_from!(u8, u8);
119cast_from!(u8, u16);
120cast_from!(u8, i16);
121cast_from!(u8, u32);
122cast_from!(u8, i32);
123cast_from!(u8, u64);
124cast_from!(u8, i64);
125cast_from!(u8, u128);
126cast_from!(u8, i128);
127cast_from!(u16, u16);
128cast_from!(u16, u32);
129cast_from!(u16, i32);
130cast_from!(u16, u64);
131cast_from!(u16, i64);
132cast_from!(u16, u128);
133cast_from!(u16, i128);
134cast_from!(u32, u32);
135cast_from!(u32, u64);
136cast_from!(u32, i64);
137cast_from!(u32, u128);
138cast_from!(u32, i128);
139cast_from!(u64, u64);
140cast_from!(u64, u128);
141cast_from!(u64, i128);
142cast_from!(i8, i8);
143cast_from!(i8, i16);
144cast_from!(i8, i32);
145cast_from!(i8, i64);
146cast_from!(i8, i128);
147cast_from!(i16, i16);
148cast_from!(i16, i32);
149cast_from!(i16, i64);
150cast_from!(i16, i128);
151cast_from!(i32, i32);
152cast_from!(i32, i64);
153cast_from!(i32, i128);
154cast_from!(i64, i64);
155cast_from!(i64, i128);
156
157/// A trait for attempted casts.
158///
159/// `TryCast` is like `as`, but returns `None` if
160/// the conversion can't be round-tripped.
161///
162/// Note: there may be holes in the domain of `try_cast_from`,
163/// which is probably why `TryFrom` wasn't implemented for floats in the
164/// standard library. For example, `i64::MAX` can be converted to
165/// `f64`, but `i64::MAX - 1` can't.
166pub trait TryCastFrom<T>: Sized {
167    /// Attempts to perform the cast
168    fn try_cast_from(from: T) -> Option<Self>;
169}
170
171/// Implement `TryCastFrom` for the specified types.
172/// This is only necessary for types for which `as` exists,
173/// but `TryFrom` doesn't (notably floats).
174macro_rules! try_cast_from {
175    ($from:ty, $to:ty) => {
176        impl crate::cast::TryCastFrom<$from> for $to {
177            #[allow(clippy::as_conversions)]
178            fn try_cast_from(from: $from) -> Option<$to> {
179                let to = from as $to;
180                let inverse = to as $from;
181                if from == inverse {
182                    Some(to)
183                } else {
184                    None
185                }
186            }
187        }
188    };
189}
190
191try_cast_from!(f64, i64);
192try_cast_from!(i64, f64);
193try_cast_from!(f64, u64);
194try_cast_from!(u64, f64);
195
196/// A wrapper type which ensures a signed number is non-negative.
197#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
198#[repr(transparent)]
199pub struct NonNeg<T>(T)
200where
201    T: Signed + fmt::Display;
202
203impl<T> NonNeg<T>
204where
205    T: Signed + fmt::Display,
206{
207    /// Returns the minimum value of the type.
208    pub fn min() -> NonNeg<T> {
209        NonNeg(T::zero())
210    }
211
212    /// Returns the maximum value of the type.
213    pub fn max() -> NonNeg<T>
214    where
215        T: UpperBounded,
216    {
217        NonNeg(T::max_value())
218    }
219
220    /// Attempts to construct a `NonNeg` from its underlying type.
221    ///
222    /// Returns an error if `n` is negative.
223    pub fn try_from(n: T) -> Result<NonNeg<T>, NonNegError> {
224        match n.is_negative() {
225            false => Ok(NonNeg(n)),
226            true => Err(NonNegError),
227        }
228    }
229}
230
231impl<T> fmt::Display for NonNeg<T>
232where
233    T: Signed + fmt::Display,
234{
235    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
236        self.0.fmt(f)
237    }
238}
239
240impl<T> Deref for NonNeg<T>
241where
242    T: Signed + fmt::Display,
243{
244    type Target = T;
245
246    fn deref(&self) -> &T {
247        &self.0
248    }
249}
250
251impl From<NonNeg<i64>> for u64 {
252    fn from(n: NonNeg<i64>) -> u64 {
253        u64::try_from(*n).expect("non-negative")
254    }
255}
256
257#[cfg(target_pointer_width = "64")]
258impl CastFrom<NonNeg<i64>> for usize {
259    #[allow(clippy::as_conversions)]
260    fn cast_from(from: NonNeg<i64>) -> usize {
261        usize::cast_from(u64::from(from))
262    }
263}
264
265/// An error indicating the attempted construction of a `NonNeg` with a negative
266/// number.
267#[derive(Debug, Clone)]
268pub struct NonNegError;
269
270impl fmt::Display for NonNegError {
271    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
272        f.write_str("cannot construct NonNeg from negative number")
273    }
274}
275
276impl Error for NonNegError {}