@@ -185,6 +185,24 @@ macro_rules! transmute {
185185/// assert_eq!(size_of_val(src), size_of_val(dst));
186186/// ```
187187///
188+ /// ## `#![allow(shrink)]`
189+ ///
190+ /// If `#![allow(shrink)]` is provided, `transmute_ref!` additionally supports
191+ /// transmutations that shrink the size of the referent; e.g.:
192+ ///
193+ /// ```
194+ /// # use zerocopy::transmute_ref;
195+ /// # use core::mem::size_of_val; // Not in the prelude on our MSRV
196+ /// let src: &[[u8; 3]] = &[[0, 1, 2], [3, 4, 5], [6, 7, 8]][..];
197+ /// let dst: &[[u8; 2]] = transmute_ref!(#![allow(shrink)] src);
198+ ///
199+ /// assert_eq!(src.len(), 3);
200+ /// assert_eq!(dst.len(), 4);
201+ /// assert_eq!(size_of_val(src), 9);
202+ /// assert_eq!(size_of_val(dst), 8);
203+ /// assert_eq!(dst, [[0, 1], [2, 3], [4, 5], [6, 7]]);
204+ /// ```
205+ ///
188206/// # Errors
189207///
190208/// Violations of the alignment and size compatibility checks are detected
@@ -271,7 +289,18 @@ macro_rules! transmute {
271289/// `Dst: Sized`.
272290#[ macro_export]
273291macro_rules! transmute_ref {
274- ( $e: expr) => { {
292+ ( #![ allow( shrink) ] $e: expr) => {
293+ $crate:: __transmute_ref_inner!( true , $e)
294+ } ;
295+ ( $e: expr) => {
296+ $crate:: __transmute_ref_inner!( false , $e)
297+ } ;
298+ }
299+
300+ #[ macro_export]
301+ #[ doc( hidden) ]
302+ macro_rules! __transmute_ref_inner {
303+ ( $allow_shrink: literal, $e: expr) => { {
275304 // NOTE: This must be a macro (rather than a function with trait bounds)
276305 // because there's no way, in a generic context, to enforce that two
277306 // types have the same size or alignment.
@@ -310,10 +339,10 @@ macro_rules! transmute_ref {
310339 // - `Src: IntoBytes + Immutable`
311340 // - `Dst: FromBytes + Immutable`
312341 unsafe {
313- t. transmute_ref( )
342+ t. transmute_ref:: <$allow_shrink> ( )
314343 }
315344 }
316- } }
345+ } } ;
317346}
318347
319348/// Safely transmutes a mutable reference of one type to a mutable reference of
@@ -359,6 +388,29 @@ macro_rules! transmute_ref {
359388/// assert_eq!(size_of_val(src), dst_size);
360389/// ```
361390///
391+ /// ## `#![allow(shrink)]`
392+ ///
393+ /// If `#![allow(shrink)]` is provided, `transmute_mut!` additionally supports
394+ /// transmutations that shrink the size of the referent; e.g.:
395+ ///
396+ /// ```
397+ /// # use zerocopy::transmute_mut;
398+ /// # use core::mem::size_of_val; // Not in the prelude on our MSRV
399+ /// let src: &mut [[u8; 3]] = &mut [[0, 1, 2], [3, 4, 5], [6, 7, 8]][..];
400+ /// let dst: &mut [[u8; 2]] = transmute_mut!(#![allow(shrink)] src);
401+ ///
402+ ///
403+ /// let dst_len = dst.len();
404+ /// let dst_size = size_of_val(dst);
405+ /// assert_eq!(dst, [[0, 1], [2, 3], [4, 5], [6, 7]]);
406+ ///
407+ /// assert_eq!(src.len(), 3);
408+ /// assert_eq!(dst_len, 4);
409+ ///
410+ /// assert_eq!(size_of_val(src), 9);
411+ /// assert_eq!(dst_size, 8);
412+ /// ```
413+ ///
362414/// # Errors
363415///
364416/// Violations of the alignment and size compatibility checks are detected
@@ -447,7 +499,18 @@ macro_rules! transmute_ref {
447499/// ```
448500#[ macro_export]
449501macro_rules! transmute_mut {
450- ( $e: expr) => { {
502+ ( #![ allow( shrink) ] $e: expr) => {
503+ $crate:: __transmute_mut_inner!( true , $e)
504+ } ;
505+ ( $e: expr) => {
506+ $crate:: __transmute_mut_inner!( false , $e)
507+ } ;
508+ }
509+
510+ #[ doc( hidden) ]
511+ #[ macro_export]
512+ macro_rules! __transmute_mut_inner {
513+ ( $allow_shrink: literal, $e: expr) => { {
451514 // NOTE: This must be a macro (rather than a function with trait bounds)
452515 // because, for backwards-compatibility on v0.8.x, we use the autoref
453516 // specialization trick to dispatch to different `transmute_mut`
@@ -461,7 +524,7 @@ macro_rules! transmute_mut {
461524 #[ allow( unused) ]
462525 use $crate:: util:: macro_util:: TransmuteMutDst as _;
463526 let t = $crate:: util:: macro_util:: Wrap :: new( e) ;
464- t. transmute_mut( )
527+ t. transmute_mut:: <$allow_shrink> ( )
465528 } }
466529}
467530
@@ -1209,6 +1272,11 @@ mod tests {
12091272 let slice_of_u16s: & [ U16 ] = <[ U16 ] >:: ref_from_bytes ( & [ 0 , 1 , 2 , 3 , 4 , 5 ] [ ..] ) . unwrap ( ) ;
12101273 assert_eq ! ( x, slice_of_u16s) ;
12111274
1275+ // Test that transmuting from a larger sized type to a smaller sized
1276+ // type works.
1277+ let x: & u8 = transmute_ref ! ( #![ allow( shrink) ] & 0u16 ) ;
1278+ assert_eq ! ( * x, 0 ) ;
1279+
12121280 // Test that transmuting from a type with larger trailing slice offset
12131281 // and larger trailing slice element works.
12141282 let bytes = & [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] [ ..] ;
@@ -1217,6 +1285,15 @@ mod tests {
12171285 let x: & SliceDst < U16 , u8 > = transmute_ref ! ( slice_dst_big) ;
12181286 assert_eq ! ( x, slice_dst_small) ;
12191287
1288+ let bytes = & [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] [ ..] ;
1289+ let slice_dst_big = SliceDst :: < [ u8 ; 4 ] , [ u8 ; 4 ] > :: ref_from_bytes ( bytes) . unwrap ( ) ;
1290+ let slice_dst_small = SliceDst :: < [ u8 ; 3 ] , [ u8 ; 3 ] > :: ref_from_bytes ( & bytes[ ..6 ] ) . unwrap ( ) ;
1291+ let x: & SliceDst < [ u8 ; 3 ] , [ u8 ; 3 ] > = transmute_ref ! (
1292+ #![ allow( shrink) ]
1293+ slice_dst_big
1294+ ) ;
1295+ assert_eq ! ( x, slice_dst_small) ;
1296+
12201297 // Test that it's legal to transmute a reference while shrinking the
12211298 // lifetime (note that `X` has the lifetime `'static`).
12221299 let x: & [ u8 ; 8 ] = transmute_ref ! ( X ) ;
@@ -1397,6 +1474,14 @@ mod tests {
13971474 let x: & mut [ i16 ] = transmute_mut ! ( array_of_u16s) ;
13981475 assert_eq ! ( x, array_of_i16s) ;
13991476
1477+ // Test that transmuting from a larger sized type to a smaller sized
1478+ // type works.
1479+ let mut large: [ u8 ; 2 ] = [ 1 , 1 ] ;
1480+ let x: & mut u8 = transmute_mut ! ( #![ allow( shrink) ] & mut large) ;
1481+ assert_eq ! ( * x, 1 ) ;
1482+ * x = 0 ;
1483+ assert_eq ! ( large, [ 0 , 1 ] ) ;
1484+
14001485 // Test that transmuting from a type with larger trailing slice offset
14011486 // and larger trailing slice element works.
14021487 let mut bytes = [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] ;
@@ -1405,6 +1490,16 @@ mod tests {
14051490 let slice_dst_small = SliceDst :: < U16 , u8 > :: mut_from_bytes ( & mut bytes[ ..] ) . unwrap ( ) ;
14061491 let x: & mut SliceDst < U16 , u8 > = transmute_mut ! ( slice_dst_big) ;
14071492 assert_eq ! ( x, slice_dst_small) ;
1493+
1494+ let mut bytes = [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] ;
1495+ let slice_dst_big = SliceDst :: < [ u8 ; 4 ] , [ u8 ; 4 ] > :: mut_from_bytes ( & mut bytes[ ..] ) . unwrap ( ) ;
1496+ let mut bytes = [ 0 , 1 , 2 , 3 , 4 , 5 ] ;
1497+ let slice_dst_small = SliceDst :: < [ u8 ; 3 ] , [ u8 ; 3 ] > :: mut_from_bytes ( & mut bytes[ ..] ) . unwrap ( ) ;
1498+ let x: & mut SliceDst < [ u8 ; 3 ] , [ u8 ; 3 ] > = transmute_mut ! (
1499+ #![ allow( shrink) ]
1500+ slice_dst_big
1501+ ) ;
1502+ assert_eq ! ( x, slice_dst_small) ;
14081503 }
14091504
14101505 #[ test]
0 commit comments