@@ -1939,14 +1939,16 @@ mod simd {
1939
1939
simd_arch_mod ! ( arm, int8x4_t, uint8x4_t) ;
1940
1940
}
1941
1941
1942
- // Used in `transmute!` below.
1942
+ // Used in macros below.
1943
1943
#[ doc( hidden) ]
1944
1944
pub use core:: mem:: transmute as __real_transmute;
1945
+ #[ doc( hidden) ]
1946
+ pub use core:: mem:: ManuallyDrop as __RealManuallyDrop;
1945
1947
1946
1948
/// Safely transmutes a value of one type to a value of another type of the same
1947
1949
/// size.
1948
1950
///
1949
- /// The expression `$e` must have a concrete type, `T`, which implements
1951
+ /// The expression, `$e`, must have a concrete type, `T`, which implements
1950
1952
/// `AsBytes`. The `transmute!` expression must also have a concrete type, `U`
1951
1953
/// (`U` is inferred from the calling context), and `U` must implement
1952
1954
/// `FromBytes`.
@@ -1989,6 +1991,160 @@ macro_rules! transmute {
1989
1991
} }
1990
1992
}
1991
1993
1994
+ /// Safely attempts to transmute a value of one type to a value of another type
1995
+ /// of the same size, failing if the transmute would be unsound.
1996
+ ///
1997
+ /// The expression, `$e`, must have a concrete type, `T`, which implements
1998
+ /// `AsBytes`. The `try_transmute!` expression must also have a concrete type,
1999
+ /// `Option<U>` (`U` is inferred from the calling context), and `U` must
2000
+ /// implement `TryFromBytes`.
2001
+ ///
2002
+ /// [`TryFromBytes::try_read_from`] is used to attempt to convert `$e` to the
2003
+ /// output type `U`. This will fail if the bytes of `$e` do not correspond to a
2004
+ /// valid instance of `U`.
2005
+ ///
2006
+ /// Note that the `T` produced by the expression `$e` will *not* be dropped.
2007
+ /// Semantically, its bits will be copied into a new value of type `U`, the
2008
+ /// original `T` will be forgotten, and the value of type `U` will be returned.
2009
+ ///
2010
+ /// # Examples
2011
+ ///
2012
+ /// ```rust
2013
+ /// # use zerocopy::try_transmute;
2014
+ /// assert_eq!(try_transmute!(1u8), Some(true));
2015
+ /// assert_eq!(try_transmute!(2u8), None::<bool>);
2016
+ ///
2017
+ /// assert_eq!(try_transmute!(108u32), Some('l'));
2018
+ /// assert_eq!(try_transmute!(0xD800u32), None::<char>);
2019
+ /// ```
2020
+ #[ macro_export]
2021
+ macro_rules! try_transmute {
2022
+ ( $e: expr) => { {
2023
+ // NOTE: This must be a macro (rather than a function with trait bounds)
2024
+ // because there's no way, in a generic context, to enforce that two
2025
+ // types have the same size. `core::mem::transmute` uses compiler magic
2026
+ // to enforce this so long as the types are concrete.
2027
+
2028
+ let e = $e;
2029
+ if false {
2030
+ // This branch, though never taken, ensures that the type of `e` is
2031
+ // `AsBytes` and that the type of this macro invocation expression
2032
+ // is `TryFromBytes`.
2033
+ const fn transmute<T : $crate:: AsBytes , U : $crate:: TryFromBytes >( _t: T ) -> Option <U > {
2034
+ unreachable!( )
2035
+ }
2036
+ transmute( e)
2037
+ } else {
2038
+ // TODO: What's the correct drop behavior on `None`? Does this just
2039
+ // behave like `mem::forget` in that case?
2040
+ let m = $crate:: __RealManuallyDrop:: new( e) ;
2041
+ $crate:: TryFromBytes :: try_read_from( $crate:: AsBytes :: as_bytes( & m) )
2042
+ }
2043
+ } }
2044
+ }
2045
+
2046
+ /// Safely attempts to transmute a reference of one type to a reference of
2047
+ /// another type, failing if the transmute would be unsound.
2048
+ ///
2049
+ /// The expression, `$e`, must have a concrete type, `&T`, where `T: AsBytes`.
2050
+ /// The `try_transmute_ref!` expression must also have a concrete type,
2051
+ /// `Option<&U>` (`U` is inferred from the calling context), and `U` must
2052
+ /// implement `TryFromBytes`.
2053
+ ///
2054
+ /// [`TryFromBytes::try_from_ref`] is used to attempt to convert `$e` to the
2055
+ /// output reference type `&U`. This will fail if `$e` is not the right size, is
2056
+ /// not properly aligned, or if the bytes of `$e` do not correspond to a valid
2057
+ /// instance of `U`.
2058
+ ///
2059
+ /// Note that, if `U` is an unsized type, there will be multiple sizes for `$e`
2060
+ /// which correspond to valid values of `U`.
2061
+ ///
2062
+ /// # Examples
2063
+ ///
2064
+ /// ```rust
2065
+ /// # use zerocopy::try_transmute_ref;
2066
+ /// # use zerocopy::AsBytes as _;
2067
+ /// let s: Option<&str> = try_transmute_ref!(&[104u8, 101, 108, 108, 111]);
2068
+ /// assert_eq!(s, Some("hello"));
2069
+ ///
2070
+ /// // Invalid UTF-8
2071
+ /// assert_eq!(try_transmute_ref!(&0xFFFFFFFFu32), None::<&str>);
2072
+ ///
2073
+ /// // Not enough bytes for a `u8`
2074
+ /// assert_eq!(try_transmute_ref!(&()), None::<&u8>);
2075
+ ///
2076
+ /// // Valid `&[[u8; 2]]` slices could be 2 or 4 bytes long,
2077
+ /// // but not 3.
2078
+ /// assert_eq!(try_transmute_ref!(&[0u8, 1, 2]), None::<&[[u8; 2]]>);
2079
+ ///
2080
+ /// // Guaranteed to be invalidly-aligned so long as
2081
+ /// // `align_of::<u16>() == 2` and `align_of::<u32>() >= 2`
2082
+ /// // (this is true on most targets, but it isn't guaranteed).
2083
+ /// assert_eq!(try_transmute_ref!(&0u32.as_bytes()[1..]), None::<&u16>);
2084
+ /// ```
2085
+ #[ macro_export]
2086
+ macro_rules! try_transmute_ref {
2087
+ ( $e: expr) => {
2088
+ $crate:: TryFromBytes :: try_from_ref( $crate:: AsBytes :: as_bytes( $e) )
2089
+ } ;
2090
+ }
2091
+
2092
+ /// Safely attempts to transmute a mutable reference of one type to a mutable
2093
+ /// reference of another type, failing if the transmute would be unsound.
2094
+ ///
2095
+ /// The expression, `$e`, must have a concrete type, `&mut T`, where `T:
2096
+ /// FromBytes + AsBytes`. The `try_transmute_ref!` expression must also have a
2097
+ /// concrete type, `Option<&mut U>` (`U` is inferred from the calling context),
2098
+ /// and `U` must implement `TryFromBytes`.
2099
+ ///
2100
+ /// [`TryFromBytes::try_from_mut`] is used to attempt to convert `$e` to the
2101
+ /// output reference type, `&mut U`. This will fail if `$e` is not the right
2102
+ /// size, is not properly aligned, or if the bytes of `$e` do not correspond to
2103
+ /// a valid instance of `U`.
2104
+ ///
2105
+ /// Note that, if `U` is an unsized type, there will be multiple sizes for `$e`
2106
+ /// which correspond to valid values of `U`.
2107
+ ///
2108
+ /// # Examples
2109
+ ///
2110
+ /// ```rust
2111
+ /// # use zerocopy::try_transmute_mut;
2112
+ /// # use zerocopy::AsBytes as _;
2113
+ /// let bytes = &mut [104u8, 101, 108, 108, 111];
2114
+ /// let mut s = try_transmute_mut!(bytes);
2115
+ /// assert_eq!(s, Some(String::from("hello").as_mut_str()));
2116
+ ///
2117
+ /// // Mutations to the transmuted reference are reflected
2118
+ /// // in the original reference.
2119
+ /// s.as_mut().unwrap().make_ascii_uppercase();
2120
+ /// assert_eq!(bytes, &[72, 69, 76, 76, 79]);
2121
+ ///
2122
+ /// // Invalid UTF-8
2123
+ /// let mut u = 0xFFFFFFFFu32;
2124
+ /// assert_eq!(try_transmute_mut!(&mut u), None::<&mut str>);
2125
+ ///
2126
+ /// // Not enough bytes for a `u8`
2127
+ /// let mut tuple = ();
2128
+ /// assert_eq!(try_transmute_mut!(&mut tuple), None::<&mut u8>);
2129
+ ///
2130
+ /// // Valid `&mut [[u8; 2]]` slices could be 2 or 4 bytes
2131
+ /// // long, but not 3.
2132
+ /// let bytes = &mut [0u8, 1, 2];
2133
+ /// assert_eq!(try_transmute_mut!(bytes), None::<&mut [[u8; 2]]>);
2134
+ ///
2135
+ /// // Guaranteed to be invalidly-aligned so long as
2136
+ /// // `align_of::<u16>() == 2` and `align_of::<u32>() >= 2`
2137
+ /// // (this is true on most targets, but it isn't guaranteed).
2138
+ /// let mut u = 0u32;
2139
+ /// assert_eq!(try_transmute_mut!(&mut u.as_bytes_mut()[1..]), None::<&mut u16>);
2140
+ /// ```
2141
+ #[ macro_export]
2142
+ macro_rules! try_transmute_mut {
2143
+ ( $e: expr) => {
2144
+ $crate:: TryFromBytes :: try_from_mut( $crate:: AsBytes :: as_bytes_mut( $e) )
2145
+ } ;
2146
+ }
2147
+
1992
2148
/// A typed reference derived from a byte slice.
1993
2149
///
1994
2150
/// A `Ref<B, T>` is a reference to a `T` which is stored in a byte slice, `B`.
@@ -3599,10 +3755,16 @@ mod tests {
3599
3755
// Test that memory is transmuted as expected.
3600
3756
let array_of_u8s = [ 0u8 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] ;
3601
3757
let array_of_arrays = [ [ 0 , 1 ] , [ 2 , 3 ] , [ 4 , 5 ] , [ 6 , 7 ] ] ;
3758
+
3602
3759
let x: [ [ u8 ; 2 ] ; 4 ] = transmute ! ( array_of_u8s) ;
3603
3760
assert_eq ! ( x, array_of_arrays) ;
3761
+ let x: Option < [ [ u8 ; 2 ] ; 4 ] > = try_transmute ! ( array_of_u8s) ;
3762
+ assert_eq ! ( x, Some ( array_of_arrays) ) ;
3763
+
3604
3764
let x: [ u8 ; 8 ] = transmute ! ( array_of_arrays) ;
3605
3765
assert_eq ! ( x, array_of_u8s) ;
3766
+ let x: Option < [ u8 ; 8 ] > = try_transmute ! ( array_of_arrays) ;
3767
+ assert_eq ! ( x, Some ( array_of_u8s) ) ;
3606
3768
3607
3769
// Test that the source expression's value is forgotten rather than
3608
3770
// dropped.
@@ -3615,12 +3777,37 @@ mod tests {
3615
3777
}
3616
3778
}
3617
3779
let _: ( ) = transmute ! ( PanicOnDrop ( ( ) ) ) ;
3780
+ let _: Option < ( ) > = try_transmute ! ( PanicOnDrop ( ( ) ) ) ;
3618
3781
3619
3782
// Test that `transmute!` is legal in a const context.
3620
3783
const ARRAY_OF_U8S : [ u8 ; 8 ] = [ 0u8 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] ;
3621
3784
const ARRAY_OF_ARRAYS : [ [ u8 ; 2 ] ; 4 ] = [ [ 0 , 1 ] , [ 2 , 3 ] , [ 4 , 5 ] , [ 6 , 7 ] ] ;
3622
3785
const X : [ [ u8 ; 2 ] ; 4 ] = transmute ! ( ARRAY_OF_U8S ) ;
3623
3786
assert_eq ! ( X , ARRAY_OF_ARRAYS ) ;
3787
+
3788
+ // Test fallible transmutations with `try_transmute!`.
3789
+ let mut b: Option < bool > = try_transmute ! ( 0u8 ) ;
3790
+ assert_eq ! ( b, Some ( false ) ) ;
3791
+ b = try_transmute ! ( 1u8 ) ;
3792
+ assert_eq ! ( b, Some ( true ) ) ;
3793
+ b = try_transmute ! ( 2u8 ) ;
3794
+ assert_eq ! ( b, None ) ;
3795
+ }
3796
+
3797
+ #[ test]
3798
+ fn test_try_transmute_ref_mut ( ) {
3799
+ // These macros are dead-simple thin wrappers which delegate to other
3800
+ // traits. We only have this test to ensure that the macros are uesd
3801
+ // somewhere so our tests will break if the paths to various items
3802
+ // break.
3803
+ let x: Option < & [ u8 ; 2 ] > = try_transmute_ref ! ( & 0xFFFFu16 ) ;
3804
+ assert_eq ! ( x, Some ( & [ 255 , 255 ] ) ) ;
3805
+
3806
+ let mut u = 0xFFFFu16 ;
3807
+ let x: Option < & mut [ u8 ; 2 ] > = try_transmute_mut ! ( & mut u) ;
3808
+ assert_eq ! ( x, Some ( & mut [ 255 , 255 ] ) ) ;
3809
+ * x. unwrap ( ) = [ 0 , 0 ] ;
3810
+ assert_eq ! ( u, 0 ) ;
3624
3811
}
3625
3812
3626
3813
#[ test]
0 commit comments