@@ -1954,14 +1954,16 @@ mod simd {
19541954 simd_arch_mod ! ( arm, int8x4_t, uint8x4_t) ;
19551955}
19561956
1957- // Used in `transmute!` below.
1957+ // Used in macros below.
19581958#[ doc( hidden) ]
19591959pub use core:: mem:: transmute as __real_transmute;
1960+ #[ doc( hidden) ]
1961+ pub use core:: mem:: ManuallyDrop as __RealManuallyDrop;
19601962
19611963/// Safely transmutes a value of one type to a value of another type of the same
19621964/// size.
19631965///
1964- /// The expression `$e` must have a concrete type, `T`, which implements
1966+ /// The expression, `$e`, must have a concrete type, `T`, which implements
19651967/// `AsBytes`. The `transmute!` expression must also have a concrete type, `U`
19661968/// (`U` is inferred from the calling context), and `U` must implement
19671969/// `FromBytes`.
@@ -2004,6 +2006,168 @@ macro_rules! transmute {
20042006 } }
20052007}
20062008
2009+ /// Safely attempts to transmute a value of one type to a value of another type
2010+ /// of the same size, failing if the transmute would be unsound.
2011+ ///
2012+ /// The expression, `$e`, must have a concrete type, `T`, which implements
2013+ /// `AsBytes`. The `try_transmute!` expression must also have a concrete type,
2014+ /// `Option<U>` (`U` is inferred from the calling context), and `U` must
2015+ /// implement `TryFromBytes`.
2016+ ///
2017+ /// [`TryFromBytes::try_read_from`] is used to attempt to convert `$e` to the
2018+ /// output type `U`. This will fail if the bytes of `$e` do not correspond to a
2019+ /// valid instance of `U`.
2020+ ///
2021+ /// Note that the `T` produced by the expression `$e` will *not* be dropped.
2022+ /// Semantically, its bits will be copied into a new value of type `U`, the
2023+ /// original `T` will be forgotten, and the value of type `U` will be returned.
2024+ ///
2025+ /// # Examples
2026+ ///
2027+ /// ```rust
2028+ /// # use zerocopy::try_transmute;
2029+ /// assert_eq!(try_transmute!(1u8), Some(true));
2030+ /// assert_eq!(try_transmute!(2u8), None::<bool>);
2031+ ///
2032+ /// assert_eq!(try_transmute!(108u32), Some('l'));
2033+ /// assert_eq!(try_transmute!(0xD800u32), None::<char>);
2034+ /// ```
2035+ #[ macro_export]
2036+ macro_rules! try_transmute {
2037+ ( $e: expr) => { {
2038+ // NOTE: This must be a macro (rather than a function with trait bounds)
2039+ // because there's no way, in a generic context, to enforce that two
2040+ // types have the same size. `core::mem::transmute` uses compiler magic
2041+ // to enforce this so long as the types are concrete.
2042+
2043+ let e = $e;
2044+ if false {
2045+ // This branch, though never taken, ensures that the type of `e` is
2046+ // `AsBytes` and that the type of this macro invocation expression
2047+ // is `TryFromBytes`.
2048+ const fn transmute<T : $crate:: AsBytes , U : $crate:: TryFromBytes >( _t: T ) -> U {
2049+ unreachable!( )
2050+ }
2051+ Some ( transmute( e) )
2052+ } else if false {
2053+ // Though never executed, this ensures that the source and
2054+ // destination types have the same size. This isn't strictly
2055+ // necessary for soundness, but it turns what would otherwise be
2056+ // runtime errors into compile-time errors.
2057+ //
2058+ // SAFETY: This branch never executes.
2059+ Some ( unsafe { $crate:: __real_transmute( e) } )
2060+ } else {
2061+ // TODO: What's the correct drop behavior on `None`? Does this just
2062+ // behave like `mem::forget` in that case?
2063+ let m = $crate:: __RealManuallyDrop:: new( e) ;
2064+ $crate:: TryFromBytes :: try_read_from( $crate:: AsBytes :: as_bytes( & m) )
2065+ }
2066+ } }
2067+ }
2068+
2069+ /// Safely attempts to transmute a reference of one type to a reference of
2070+ /// another type, failing if the transmute would be unsound.
2071+ ///
2072+ /// The expression, `$e`, must have a concrete type, `&T`, where `T: AsBytes`.
2073+ /// The `try_transmute_ref!` expression must also have a concrete type,
2074+ /// `Option<&U>` (`U` is inferred from the calling context), and `U` must
2075+ /// implement `TryFromBytes`.
2076+ ///
2077+ /// [`TryFromBytes::try_from_ref`] is used to attempt to convert `$e` to the
2078+ /// output reference type `&U`. This will fail if `$e` is not the right size, is
2079+ /// not properly aligned, or if the bytes of `$e` do not correspond to a valid
2080+ /// instance of `U`.
2081+ ///
2082+ /// Note that, if `U` is an unsized type, there will be multiple sizes for `$e`
2083+ /// which correspond to valid values of `U`.
2084+ ///
2085+ /// # Examples
2086+ ///
2087+ /// ```rust
2088+ /// # use zerocopy::try_transmute_ref;
2089+ /// # use zerocopy::AsBytes as _;
2090+ /// let s: Option<&str> = try_transmute_ref!(&[104u8, 101, 108, 108, 111]);
2091+ /// assert_eq!(s, Some("hello"));
2092+ ///
2093+ /// // Invalid UTF-8
2094+ /// assert_eq!(try_transmute_ref!(&0xFFFFFFFFu32), None::<&str>);
2095+ ///
2096+ /// // Not enough bytes for a `u8`
2097+ /// assert_eq!(try_transmute_ref!(&()), None::<&u8>);
2098+ ///
2099+ /// // Valid `&[[u8; 2]]` slices could be 2 or 4 bytes long,
2100+ /// // but not 3.
2101+ /// assert_eq!(try_transmute_ref!(&[0u8, 1, 2]), None::<&[[u8; 2]]>);
2102+ ///
2103+ /// // Guaranteed to be invalidly-aligned so long as
2104+ /// // `align_of::<u16>() == 2` and `align_of::<u32>() >= 2`
2105+ /// // (this is true on most targets, but it isn't guaranteed).
2106+ /// assert_eq!(try_transmute_ref!(&0u32.as_bytes()[1..]), None::<&u16>);
2107+ /// ```
2108+ #[ macro_export]
2109+ macro_rules! try_transmute_ref {
2110+ ( $e: expr) => {
2111+ $crate:: TryFromBytes :: try_from_ref( $crate:: AsBytes :: as_bytes( $e) )
2112+ } ;
2113+ }
2114+
2115+ /// Safely attempts to transmute a mutable reference of one type to a mutable
2116+ /// reference of another type, failing if the transmute would be unsound.
2117+ ///
2118+ /// The expression, `$e`, must have a concrete type, `&mut T`, where `T:
2119+ /// FromBytes + AsBytes`. The `try_transmute_ref!` expression must also have a
2120+ /// concrete type, `Option<&mut U>` (`U` is inferred from the calling context),
2121+ /// and `U` must implement `TryFromBytes`.
2122+ ///
2123+ /// [`TryFromBytes::try_from_mut`] is used to attempt to convert `$e` to the
2124+ /// output reference type, `&mut U`. This will fail if `$e` is not the right
2125+ /// size, is not properly aligned, or if the bytes of `$e` do not correspond to
2126+ /// a valid instance of `U`.
2127+ ///
2128+ /// Note that, if `U` is an unsized type, there will be multiple sizes for `$e`
2129+ /// which correspond to valid values of `U`.
2130+ ///
2131+ /// # Examples
2132+ ///
2133+ /// ```rust
2134+ /// # use zerocopy::try_transmute_mut;
2135+ /// # use zerocopy::AsBytes as _;
2136+ /// let bytes = &mut [104u8, 101, 108, 108, 111];
2137+ /// let mut s = try_transmute_mut!(bytes);
2138+ /// assert_eq!(s, Some(String::from("hello").as_mut_str()));
2139+ ///
2140+ /// // Mutations to the transmuted reference are reflected
2141+ /// // in the original reference.
2142+ /// s.as_mut().unwrap().make_ascii_uppercase();
2143+ /// assert_eq!(bytes, &[72, 69, 76, 76, 79]);
2144+ ///
2145+ /// // Invalid UTF-8
2146+ /// let mut u = 0xFFFFFFFFu32;
2147+ /// assert_eq!(try_transmute_mut!(&mut u), None::<&mut str>);
2148+ ///
2149+ /// // Not enough bytes for a `u8`
2150+ /// let mut tuple = ();
2151+ /// assert_eq!(try_transmute_mut!(&mut tuple), None::<&mut u8>);
2152+ ///
2153+ /// // Valid `&mut [[u8; 2]]` slices could be 2 or 4 bytes
2154+ /// // long, but not 3.
2155+ /// let bytes = &mut [0u8, 1, 2];
2156+ /// assert_eq!(try_transmute_mut!(bytes), None::<&mut [[u8; 2]]>);
2157+ ///
2158+ /// // Guaranteed to be invalidly-aligned so long as
2159+ /// // `align_of::<u16>() == 2` and `align_of::<u32>() >= 2`
2160+ /// // (this is true on most targets, but it isn't guaranteed).
2161+ /// let mut u = 0u32;
2162+ /// assert_eq!(try_transmute_mut!(&mut u.as_bytes_mut()[1..]), None::<&mut u16>);
2163+ /// ```
2164+ #[ macro_export]
2165+ macro_rules! try_transmute_mut {
2166+ ( $e: expr) => {
2167+ $crate:: TryFromBytes :: try_from_mut( $crate:: AsBytes :: as_bytes_mut( $e) )
2168+ } ;
2169+ }
2170+
20072171/// A typed reference derived from a byte slice.
20082172///
20092173/// A `Ref<B, T>` is a reference to a `T` which is stored in a byte slice, `B`.
@@ -3685,10 +3849,16 @@ mod tests {
36853849 // Test that memory is transmuted as expected.
36863850 let array_of_u8s = [ 0u8 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] ;
36873851 let array_of_arrays = [ [ 0 , 1 ] , [ 2 , 3 ] , [ 4 , 5 ] , [ 6 , 7 ] ] ;
3852+
36883853 let x: [ [ u8 ; 2 ] ; 4 ] = transmute ! ( array_of_u8s) ;
36893854 assert_eq ! ( x, array_of_arrays) ;
3855+ let x: Option < [ [ u8 ; 2 ] ; 4 ] > = try_transmute ! ( array_of_u8s) ;
3856+ assert_eq ! ( x, Some ( array_of_arrays) ) ;
3857+
36903858 let x: [ u8 ; 8 ] = transmute ! ( array_of_arrays) ;
36913859 assert_eq ! ( x, array_of_u8s) ;
3860+ let x: Option < [ u8 ; 8 ] > = try_transmute ! ( array_of_arrays) ;
3861+ assert_eq ! ( x, Some ( array_of_u8s) ) ;
36923862
36933863 // Test that the source expression's value is forgotten rather than
36943864 // dropped.
@@ -3701,12 +3871,37 @@ mod tests {
37013871 }
37023872 }
37033873 let _: ( ) = transmute ! ( PanicOnDrop ( ( ) ) ) ;
3874+ let _: Option < ( ) > = try_transmute ! ( PanicOnDrop ( ( ) ) ) ;
37043875
37053876 // Test that `transmute!` is legal in a const context.
37063877 const ARRAY_OF_U8S : [ u8 ; 8 ] = [ 0u8 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] ;
37073878 const ARRAY_OF_ARRAYS : [ [ u8 ; 2 ] ; 4 ] = [ [ 0 , 1 ] , [ 2 , 3 ] , [ 4 , 5 ] , [ 6 , 7 ] ] ;
37083879 const X : [ [ u8 ; 2 ] ; 4 ] = transmute ! ( ARRAY_OF_U8S ) ;
37093880 assert_eq ! ( X , ARRAY_OF_ARRAYS ) ;
3881+
3882+ // Test fallible transmutations with `try_transmute!`.
3883+ let mut b: Option < bool > = try_transmute ! ( 0u8 ) ;
3884+ assert_eq ! ( b, Some ( false ) ) ;
3885+ b = try_transmute ! ( 1u8 ) ;
3886+ assert_eq ! ( b, Some ( true ) ) ;
3887+ b = try_transmute ! ( 2u8 ) ;
3888+ assert_eq ! ( b, None ) ;
3889+ }
3890+
3891+ #[ test]
3892+ fn test_try_transmute_ref_mut ( ) {
3893+ // These macros are dead-simple thin wrappers which delegate to other
3894+ // traits. We only have this test to ensure that the macros are uesd
3895+ // somewhere so our tests will break if the paths to various items
3896+ // break.
3897+ let x: Option < & [ u8 ; 2 ] > = try_transmute_ref ! ( & 0xFFFFu16 ) ;
3898+ assert_eq ! ( x, Some ( & [ 255 , 255 ] ) ) ;
3899+
3900+ let mut u = 0xFFFFu16 ;
3901+ let x: Option < & mut [ u8 ; 2 ] > = try_transmute_mut ! ( & mut u) ;
3902+ assert_eq ! ( x, Some ( & mut [ 255 , 255 ] ) ) ;
3903+ * x. unwrap ( ) = [ 0 , 0 ] ;
3904+ assert_eq ! ( u, 0 ) ;
37103905 }
37113906
37123907 #[ test]
0 commit comments