@@ -179,6 +179,24 @@ macro_rules! __transmute_inner {
179179/// assert_eq!(size_of_val(src), size_of_val(dst));
180180/// ```
181181///
182+ /// ## `#![allow(shrink)]`
183+ ///
184+ /// If `#![allow(shrink)]` is provided, `transmute_ref!` additionally supports
185+ /// transmutations that shrink the size of the referent; e.g.:
186+ ///
187+ /// ```
188+ /// # use zerocopy::transmute_ref;
189+ /// # use core::mem::size_of_val; // Not in the prelude on our MSRV
190+ /// let src: &[[u8; 3]] = &[[0, 1, 2], [3, 4, 5], [6, 7, 8]][..];
191+ /// let dst: &[[u8; 2]] = transmute_ref!(#![allow(shrink)] src);
192+ ///
193+ /// assert_eq!(src.len(), 3);
194+ /// assert_eq!(dst.len(), 4);
195+ /// assert_eq!(size_of_val(src), 9);
196+ /// assert_eq!(size_of_val(dst), 8);
197+ /// assert_eq!(dst, [[0, 1], [2, 3], [4, 5], [6, 7]]);
198+ /// ```
199+ ///
182200/// # Errors
183201///
184202/// Violations of the alignment and size compatibility checks are detected
@@ -265,7 +283,18 @@ macro_rules! __transmute_inner {
265283/// `Dst: Sized`.
266284#[ macro_export]
267285macro_rules! transmute_ref {
268- ( $e: expr) => { {
286+ ( #![ allow( shrink) ] $e: expr) => {
287+ $crate:: __transmute_ref_inner!( true , $e)
288+ } ;
289+ ( $e: expr) => {
290+ $crate:: __transmute_ref_inner!( false , $e)
291+ } ;
292+ }
293+
294+ #[ macro_export]
295+ #[ doc( hidden) ]
296+ macro_rules! __transmute_ref_inner {
297+ ( $allow_shrink: literal, $e: expr) => { {
269298 // NOTE: This must be a macro (rather than a function with trait bounds)
270299 // because there's no way, in a generic context, to enforce that two
271300 // types have the same size or alignment.
@@ -304,10 +333,10 @@ macro_rules! transmute_ref {
304333 // - `Src: IntoBytes + Immutable`
305334 // - `Dst: FromBytes + Immutable`
306335 unsafe {
307- t. transmute_ref( )
336+ t. transmute_ref:: <$allow_shrink> ( )
308337 }
309338 }
310- } }
339+ } } ;
311340}
312341
313342/// Safely transmutes a mutable reference of one type to a mutable reference of
@@ -353,6 +382,29 @@ macro_rules! transmute_ref {
353382/// assert_eq!(size_of_val(src), dst_size);
354383/// ```
355384///
385+ /// ## `#![allow(shrink)]`
386+ ///
387+ /// If `#![allow(shrink)]` is provided, `transmute_mut!` additionally supports
388+ /// transmutations that shrink the size of the referent; e.g.:
389+ ///
390+ /// ```
391+ /// # use zerocopy::transmute_mut;
392+ /// # use core::mem::size_of_val; // Not in the prelude on our MSRV
393+ /// let src: &mut [[u8; 3]] = &mut [[0, 1, 2], [3, 4, 5], [6, 7, 8]][..];
394+ /// let dst: &mut [[u8; 2]] = transmute_mut!(#![allow(shrink)] src);
395+ ///
396+ ///
397+ /// let dst_len = dst.len();
398+ /// let dst_size = size_of_val(dst);
399+ /// assert_eq!(dst, [[0, 1], [2, 3], [4, 5], [6, 7]]);
400+ ///
401+ /// assert_eq!(src.len(), 3);
402+ /// assert_eq!(dst_len, 4);
403+ ///
404+ /// assert_eq!(size_of_val(src), 9);
405+ /// assert_eq!(dst_size, 8);
406+ /// ```
407+ ///
356408/// # Errors
357409///
358410/// Violations of the alignment and size compatibility checks are detected
@@ -441,7 +493,18 @@ macro_rules! transmute_ref {
441493/// ```
442494#[ macro_export]
443495macro_rules! transmute_mut {
444- ( $e: expr) => { {
496+ ( #![ allow( shrink) ] $e: expr) => {
497+ $crate:: __transmute_mut_inner!( true , $e)
498+ } ;
499+ ( $e: expr) => {
500+ $crate:: __transmute_mut_inner!( false , $e)
501+ } ;
502+ }
503+
504+ #[ doc( hidden) ]
505+ #[ macro_export]
506+ macro_rules! __transmute_mut_inner {
507+ ( $allow_shrink: literal, $e: expr) => { {
445508 // NOTE: This must be a macro (rather than a function with trait bounds)
446509 // because, for backwards-compatibility on v0.8.x, we use the autoref
447510 // specialization trick to dispatch to different `transmute_mut`
@@ -455,7 +518,7 @@ macro_rules! transmute_mut {
455518 #[ allow( unused) ]
456519 use $crate:: util:: macro_util:: TransmuteMutDst as _;
457520 let t = $crate:: util:: macro_util:: Wrap :: new( e) ;
458- t. transmute_mut( )
521+ t. transmute_mut:: <$allow_shrink> ( )
459522 } }
460523}
461524
@@ -1203,6 +1266,11 @@ mod tests {
12031266 let slice_of_u16s: & [ U16 ] = <[ U16 ] >:: ref_from_bytes ( & [ 0 , 1 , 2 , 3 , 4 , 5 ] [ ..] ) . unwrap ( ) ;
12041267 assert_eq ! ( x, slice_of_u16s) ;
12051268
1269+ // Test that transmuting from a larger sized type to a smaller sized
1270+ // type works.
1271+ let x: & u8 = transmute_ref ! ( #![ allow( shrink) ] & 0u16 ) ;
1272+ assert_eq ! ( * x, 0 ) ;
1273+
12061274 // Test that transmuting from a type with larger trailing slice offset
12071275 // and larger trailing slice element works.
12081276 let bytes = & [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] [ ..] ;
@@ -1211,6 +1279,15 @@ mod tests {
12111279 let x: & SliceDst < U16 , u8 > = transmute_ref ! ( slice_dst_big) ;
12121280 assert_eq ! ( x, slice_dst_small) ;
12131281
1282+ let bytes = & [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] [ ..] ;
1283+ let slice_dst_big = SliceDst :: < [ u8 ; 4 ] , [ u8 ; 4 ] > :: ref_from_bytes ( bytes) . unwrap ( ) ;
1284+ let slice_dst_small = SliceDst :: < [ u8 ; 3 ] , [ u8 ; 3 ] > :: ref_from_bytes ( & bytes[ ..6 ] ) . unwrap ( ) ;
1285+ let x: & SliceDst < [ u8 ; 3 ] , [ u8 ; 3 ] > = transmute_ref ! (
1286+ #![ allow( shrink) ]
1287+ slice_dst_big
1288+ ) ;
1289+ assert_eq ! ( x, slice_dst_small) ;
1290+
12141291 // Test that it's legal to transmute a reference while shrinking the
12151292 // lifetime (note that `X` has the lifetime `'static`).
12161293 let x: & [ u8 ; 8 ] = transmute_ref ! ( X ) ;
@@ -1391,6 +1468,14 @@ mod tests {
13911468 let x: & mut [ i16 ] = transmute_mut ! ( array_of_u16s) ;
13921469 assert_eq ! ( x, array_of_i16s) ;
13931470
1471+ // Test that transmuting from a larger sized type to a smaller sized
1472+ // type works.
1473+ let mut large: [ u8 ; 2 ] = [ 1 , 1 ] ;
1474+ let x: & mut u8 = transmute_mut ! ( #![ allow( shrink) ] & mut large) ;
1475+ assert_eq ! ( * x, 1 ) ;
1476+ * x = 0 ;
1477+ assert_eq ! ( large, [ 0 , 1 ] ) ;
1478+
13941479 // Test that transmuting from a type with larger trailing slice offset
13951480 // and larger trailing slice element works.
13961481 let mut bytes = [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] ;
@@ -1399,6 +1484,16 @@ mod tests {
13991484 let slice_dst_small = SliceDst :: < U16 , u8 > :: mut_from_bytes ( & mut bytes[ ..] ) . unwrap ( ) ;
14001485 let x: & mut SliceDst < U16 , u8 > = transmute_mut ! ( slice_dst_big) ;
14011486 assert_eq ! ( x, slice_dst_small) ;
1487+
1488+ let mut bytes = [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] ;
1489+ let slice_dst_big = SliceDst :: < [ u8 ; 4 ] , [ u8 ; 4 ] > :: mut_from_bytes ( & mut bytes[ ..] ) . unwrap ( ) ;
1490+ let mut bytes = [ 0 , 1 , 2 , 3 , 4 , 5 ] ;
1491+ let slice_dst_small = SliceDst :: < [ u8 ; 3 ] , [ u8 ; 3 ] > :: mut_from_bytes ( & mut bytes[ ..] ) . unwrap ( ) ;
1492+ let x: & mut SliceDst < [ u8 ; 3 ] , [ u8 ; 3 ] > = transmute_mut ! (
1493+ #![ allow( shrink) ]
1494+ slice_dst_big
1495+ ) ;
1496+ assert_eq ! ( x, slice_dst_small) ;
14021497 }
14031498
14041499 #[ test]
0 commit comments