@@ -227,6 +227,24 @@ macro_rules! transmute {
227227/// assert_eq!(size_of_val(src), size_of_val(dst));
228228/// ```
229229///
230+ /// ## `#![allow(shrink)]`
231+ ///
232+ /// If `#![allow(shrink)]` is provided, `transmute_ref!` additionally supports
233+ /// transmutations that shrink the size of the referent; e.g.:
234+ ///
235+ /// ```
236+ /// # use zerocopy::transmute_ref;
237+ /// # use core::mem::size_of_val; // Not in the prelude on our MSRV
238+ /// let src: &[[u8; 3]] = &[[0, 1, 2], [3, 4, 5], [6, 7, 8]][..];
239+ /// let dst: &[[u8; 2]] = transmute_ref!(#![allow(shrink)] src);
240+ ///
241+ /// assert_eq!(src.len(), 3);
242+ /// assert_eq!(dst.len(), 4);
243+ /// assert_eq!(size_of_val(src), 9);
244+ /// assert_eq!(size_of_val(dst), 8);
245+ /// assert_eq!(dst, [[0, 1], [2, 3], [4, 5], [6, 7]]);
246+ /// ```
247+ ///
230248/// # Errors
231249///
232250/// Violations of the alignment and size compatibility checks are detected
@@ -313,7 +331,18 @@ macro_rules! transmute {
313331/// `Dst: Sized`.
314332#[ macro_export]
315333macro_rules! transmute_ref {
316- ( $e: expr) => { {
334+ ( #![ allow( shrink) ] $e: expr) => {
335+ $crate:: __transmute_ref_inner!( true , $e)
336+ } ;
337+ ( $e: expr) => {
338+ $crate:: __transmute_ref_inner!( false , $e)
339+ } ;
340+ }
341+
342+ #[ macro_export]
343+ #[ doc( hidden) ]
344+ macro_rules! __transmute_ref_inner {
345+ ( $allow_shrink: literal, $e: expr) => { {
317346 // NOTE: This must be a macro (rather than a function with trait bounds)
318347 // because there's no way, in a generic context, to enforce that two
319348 // types have the same size or alignment.
@@ -352,10 +381,10 @@ macro_rules! transmute_ref {
352381 // - `Src: IntoBytes + Immutable`
353382 // - `Dst: FromBytes + Immutable`
354383 unsafe {
355- t. transmute_ref( )
384+ t. transmute_ref:: <$allow_shrink> ( )
356385 }
357386 }
358- } }
387+ } } ;
359388}
360389
361390/// Safely transmutes a mutable reference of one type to a mutable reference of
@@ -401,6 +430,29 @@ macro_rules! transmute_ref {
401430/// assert_eq!(size_of_val(src), dst_size);
402431/// ```
403432///
433+ /// ## `#![allow(shrink)]`
434+ ///
435+ /// If `#![allow(shrink)]` is provided, `transmute_mut!` additionally supports
436+ /// transmutations that shrink the size of the referent; e.g.:
437+ ///
438+ /// ```
439+ /// # use zerocopy::transmute_mut;
440+ /// # use core::mem::size_of_val; // Not in the prelude on our MSRV
441+ /// let src: &mut [[u8; 3]] = &mut [[0, 1, 2], [3, 4, 5], [6, 7, 8]][..];
442+ /// let dst: &mut [[u8; 2]] = transmute_mut!(#![allow(shrink)] src);
443+ ///
444+ ///
445+ /// let dst_len = dst.len();
446+ /// let dst_size = size_of_val(dst);
447+ /// assert_eq!(dst, [[0, 1], [2, 3], [4, 5], [6, 7]]);
448+ ///
449+ /// assert_eq!(src.len(), 3);
450+ /// assert_eq!(dst_len, 4);
451+ ///
452+ /// assert_eq!(size_of_val(src), 9);
453+ /// assert_eq!(dst_size, 8);
454+ /// ```
455+ ///
404456/// # Errors
405457///
406458/// Violations of the alignment and size compatibility checks are detected
@@ -489,7 +541,18 @@ macro_rules! transmute_ref {
489541/// ```
490542#[ macro_export]
491543macro_rules! transmute_mut {
492- ( $e: expr) => { {
544+ ( #![ allow( shrink) ] $e: expr) => {
545+ $crate:: __transmute_mut_inner!( true , $e)
546+ } ;
547+ ( $e: expr) => {
548+ $crate:: __transmute_mut_inner!( false , $e)
549+ } ;
550+ }
551+
552+ #[ doc( hidden) ]
553+ #[ macro_export]
554+ macro_rules! __transmute_mut_inner {
555+ ( $allow_shrink: literal, $e: expr) => { {
493556 // NOTE: This must be a macro (rather than a function with trait bounds)
494557 // because, for backwards-compatibility on v0.8.x, we use the autoref
495558 // specialization trick to dispatch to different `transmute_mut`
@@ -503,7 +566,7 @@ macro_rules! transmute_mut {
503566 #[ allow( unused) ]
504567 use $crate:: util:: macro_util:: TransmuteMutDst as _;
505568 let t = $crate:: util:: macro_util:: Wrap :: new( e) ;
506- t. transmute_mut( )
569+ t. transmute_mut:: <$allow_shrink> ( )
507570 } }
508571}
509572
@@ -1251,6 +1314,11 @@ mod tests {
12511314 let slice_of_u16s: & [ U16 ] = <[ U16 ] >:: ref_from_bytes ( & [ 0 , 1 , 2 , 3 , 4 , 5 ] [ ..] ) . unwrap ( ) ;
12521315 assert_eq ! ( x, slice_of_u16s) ;
12531316
1317+ // Test that transmuting from a larger sized type to a smaller sized
1318+ // type works.
1319+ let x: & u8 = transmute_ref ! ( #![ allow( shrink) ] & 0u16 ) ;
1320+ assert_eq ! ( * x, 0 ) ;
1321+
12541322 // Test that transmuting from a type with larger trailing slice offset
12551323 // and larger trailing slice element works.
12561324 let bytes = & [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] [ ..] ;
@@ -1259,6 +1327,15 @@ mod tests {
12591327 let x: & SliceDst < U16 , u8 > = transmute_ref ! ( slice_dst_big) ;
12601328 assert_eq ! ( x, slice_dst_small) ;
12611329
1330+ let bytes = & [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] [ ..] ;
1331+ let slice_dst_big = SliceDst :: < [ u8 ; 4 ] , [ u8 ; 4 ] > :: ref_from_bytes ( bytes) . unwrap ( ) ;
1332+ let slice_dst_small = SliceDst :: < [ u8 ; 3 ] , [ u8 ; 3 ] > :: ref_from_bytes ( & bytes[ ..6 ] ) . unwrap ( ) ;
1333+ let x: & SliceDst < [ u8 ; 3 ] , [ u8 ; 3 ] > = transmute_ref ! (
1334+ #![ allow( shrink) ]
1335+ slice_dst_big
1336+ ) ;
1337+ assert_eq ! ( x, slice_dst_small) ;
1338+
12621339 // Test that it's legal to transmute a reference while shrinking the
12631340 // lifetime (note that `X` has the lifetime `'static`).
12641341 let x: & [ u8 ; 8 ] = transmute_ref ! ( X ) ;
@@ -1439,6 +1516,14 @@ mod tests {
14391516 let x: & mut [ i16 ] = transmute_mut ! ( array_of_u16s) ;
14401517 assert_eq ! ( x, array_of_i16s) ;
14411518
1519+ // Test that transmuting from a larger sized type to a smaller sized
1520+ // type works.
1521+ let mut large: [ u8 ; 2 ] = [ 1 , 1 ] ;
1522+ let x: & mut u8 = transmute_mut ! ( #![ allow( shrink) ] & mut large) ;
1523+ assert_eq ! ( * x, 1 ) ;
1524+ * x = 0 ;
1525+ assert_eq ! ( large, [ 0 , 1 ] ) ;
1526+
14421527 // Test that transmuting from a type with larger trailing slice offset
14431528 // and larger trailing slice element works.
14441529 let mut bytes = [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] ;
@@ -1447,6 +1532,16 @@ mod tests {
14471532 let slice_dst_small = SliceDst :: < U16 , u8 > :: mut_from_bytes ( & mut bytes[ ..] ) . unwrap ( ) ;
14481533 let x: & mut SliceDst < U16 , u8 > = transmute_mut ! ( slice_dst_big) ;
14491534 assert_eq ! ( x, slice_dst_small) ;
1535+
1536+ let mut bytes = [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] ;
1537+ let slice_dst_big = SliceDst :: < [ u8 ; 4 ] , [ u8 ; 4 ] > :: mut_from_bytes ( & mut bytes[ ..] ) . unwrap ( ) ;
1538+ let mut bytes = [ 0 , 1 , 2 , 3 , 4 , 5 ] ;
1539+ let slice_dst_small = SliceDst :: < [ u8 ; 3 ] , [ u8 ; 3 ] > :: mut_from_bytes ( & mut bytes[ ..] ) . unwrap ( ) ;
1540+ let x: & mut SliceDst < [ u8 ; 3 ] , [ u8 ; 3 ] > = transmute_mut ! (
1541+ #![ allow( shrink) ]
1542+ slice_dst_big
1543+ ) ;
1544+ assert_eq ! ( x, slice_dst_small) ;
14501545 }
14511546
14521547 #[ test]
0 commit comments