safe_transmute/
util.rs

1//! Module containing various utility functions.
2
3
4/// Retrieve the result of a transmutation,
5/// copying the data if it could not be safely performed due to memory alignment constraints.
6///
7/// This macro, akin to `try!()`, will short-circuit certain errors by
8/// `return`ing, namely guard condition and invalid value errors.
9///
10/// When the operation fails due to either an unaligned transmutation
11/// or an incompatible vector element transmutation target,
12/// the transmutation is reattempted by byte-copying (i.e. `memcpy()`ing)
13/// the input into a newly-allocated vector.
14///
15/// This expands into a single expression of type `Cow<[T]>`,
16/// where `T` is the target type.
17///
18/// # Example
19///
20/// ```
21/// # #[macro_use]
22/// # extern crate safe_transmute;
23/// # use safe_transmute::{SingleManyGuard, transmute_many};
24/// # use safe_transmute::error::Error;
25/// # fn main() -> Result<(), Error<'static, u8, u16>> {
26/// let bytes = &[0x00, 0x01, 0x12, 0x34,
27///               0x00]; // 1 spare byte
28/// let words = try_copy!(transmute_many::<u16, SingleManyGuard>(bytes));
29///
30/// assert_eq!(*words,
31///            [u16::from_be(0x0001), u16::from_be(0x1234)]);
32/// # Ok(())
33/// # }
34/// ```
35#[cfg(feature = "alloc")]
36#[macro_export]
37macro_rules! try_copy {
38    ($res:expr) => {{
39        use $crate::alloc::borrow::Cow;  // TODO: There *has* to be a better way of doing this, right? (also below)
40
41        $res.map_err($crate::Error::from)
42            .map(Cow::from)
43            .or_else(|e| e.copy().map(Cow::Owned))?
44    }}
45}
46
47/// Retrieve the result of a non-trivial transmutation,
48/// copying the data if it could not be safely performed due to memory alignment constraints.
49///
50/// Equivalent to [`try_copy!()`](macro.try_copy.html),
51/// except for not checking that the target type is trivially transmutable.
52///
53/// # Safety
54///
55/// The source data needs to correspond to a valid contiguous sequence of
56/// `T` values.
57///
58/// # Example
59///
60/// ```
61/// # #[macro_use]
62/// # extern crate safe_transmute;
63/// # use safe_transmute::{SingleManyGuard, transmute_many};
64/// # use safe_transmute::error::Error;
65/// # fn main() -> Result<(), Error<'static, u8, u16>> {
66/// let bytes = &[0x00, 0x01, 0x12, 0x34,
67///               0x00]; // 1 spare byte
68/// unsafe {
69///     let words = try_copy_unchecked!(transmute_many::<u16, SingleManyGuard>(bytes));
70///
71///     assert_eq!(*words,
72///                [u16::from_be(0x0001), u16::from_be(0x1234)]);
73/// }
74/// # Ok(())
75/// # }
76/// ```
77#[cfg(feature = "alloc")]
78#[macro_export]
79macro_rules! try_copy_unchecked {
80    ($res:expr) => {{
81        use $crate::alloc::borrow::Cow;  // TODO: see above
82
83        $res.map_err($crate::Error::from)
84            .map(Cow::from)
85            .or_else(|e| e.copy_unchecked().map(Cow::Owned))?
86    }}
87}
88
89
90/// If the specified 32-bit float is a signaling NaN, make it a quiet NaN.
91///
92/// Based on an old version of
93/// [`f32::from_bits()`](https://github.com/rust-lang/rust/pull/39271/files#diff-f60977ab00fd9ea9ba7ac918e12a8f42R1279).
94pub fn designalise_f32(f: f32) -> f32 {
95    from_bits_f32_designalised(f.to_bits())
96}
97
98/// If the specified 64-bit float is a signaling NaN, make it a quiet NaN.
99///
100/// Based on an old version of
101/// [`f64::from_bits()`](https://github.com/rust-lang/rust/pull/39271/files#diff-2ae382eb5bbc830a6b884b8a6ba5d95fR1171).
102pub fn designalise_f64(f: f64) -> f64 {
103    from_bits_f64_designalised(f.to_bits())
104}
105
106/// Reinterpret the given bits as a 32-bit float. If the specified word is a
107/// signaling NaN once interpreted, make it a quiet NaN.
108pub fn from_bits_f32_designalised(mut bits: u32) -> f32 {
109    const EXP_MASK: u32 = 0x7F80_0000;
110    const QNAN_MASK: u32 = 0x0040_0000;
111    const FRACT_MASK: u32 = 0x007F_FFFF;
112
113    if bits & EXP_MASK == EXP_MASK && bits & FRACT_MASK != 0 {
114        // If we have a NaN value, we
115        // convert signaling NaN values to quiet NaN
116        // by setting the the highest bit of the fraction
117        bits |= QNAN_MASK;
118    }
119
120    f32::from_bits(bits)
121}
122
123/// Reinterpret the given bits as a 64-bit float. If the specified word is a
124/// signaling NaN once interpreted, make it a quiet NaN.
125pub fn from_bits_f64_designalised(mut bits: u64) -> f64 {
126    const EXP_MASK: u64 = 0x7FF0_0000_0000_0000;
127    const QNAN_MASK: u64 = 0x0001_0000_0000_0000;
128    const FRACT_MASK: u64 = 0x000F_FFFF_FFFF_FFFF;
129
130    if bits & EXP_MASK == EXP_MASK && bits & FRACT_MASK != 0 {
131        // If we have a NaN value, we
132        // convert signaling NaN values to quiet NaN
133        // by setting the the highest bit of the fraction
134        bits |= QNAN_MASK;
135    }
136
137    f64::from_bits(bits)
138}