@@ -220,6 +220,24 @@ macro_rules! transmute {
220220/// assert_eq!(size_of_val(src), size_of_val(dst));
221221/// ```
222222///
223+ /// ## `#![allow(shrink)]`
224+ ///
225+ /// If `#![allow(shrink)]` is provided, `transmute_ref!` additionally supports
226+ /// transmutations that shrink the size of the referent; e.g.:
227+ ///
228+ /// ```
229+ /// # use zerocopy::transmute_ref;
230+ /// # use core::mem::size_of_val; // Not in the prelude on our MSRV
231+ /// let src: &[[u8; 3]] = &[[0, 1, 2], [3, 4, 5], [6, 7, 8]][..];
232+ /// let dst: &[[u8; 2]] = transmute_ref!(#![allow(shrink)] src);
233+ ///
234+ /// assert_eq!(src.len(), 3);
235+ /// assert_eq!(dst.len(), 4);
236+ /// assert_eq!(size_of_val(src), 9);
237+ /// assert_eq!(size_of_val(dst), 8);
238+ /// assert_eq!(dst, [[0, 1], [2, 3], [4, 5], [6, 7]]);
239+ /// ```
240+ ///
223241/// # Errors
224242///
225243/// Violations of the alignment and size compatibility checks are detected
@@ -306,7 +324,18 @@ macro_rules! transmute {
306324/// `Dst: Sized`.
307325#[ macro_export]
308326macro_rules! transmute_ref {
309- ( $e: expr) => { {
327+ ( #![ allow( shrink) ] $e: expr) => {
328+ $crate:: __transmute_ref_inner!( true , $e)
329+ } ;
330+ ( $e: expr) => {
331+ $crate:: __transmute_ref_inner!( false , $e)
332+ } ;
333+ }
334+
335+ #[ macro_export]
336+ #[ doc( hidden) ]
337+ macro_rules! __transmute_ref_inner {
338+ ( $allow_shrink: literal, $e: expr) => { {
310339 // NOTE: This must be a macro (rather than a function with trait bounds)
311340 // because there's no way, in a generic context, to enforce that two
312341 // types have the same size or alignment.
@@ -345,10 +374,10 @@ macro_rules! transmute_ref {
345374 // - `Src: IntoBytes + Immutable`
346375 // - `Dst: FromBytes + Immutable`
347376 unsafe {
348- t. transmute_ref( )
377+ t. transmute_ref:: <$allow_shrink> ( )
349378 }
350379 }
351- } }
380+ } } ;
352381}
353382
354383/// Safely transmutes a mutable reference of one type to a mutable reference of
@@ -394,6 +423,29 @@ macro_rules! transmute_ref {
394423/// assert_eq!(size_of_val(src), dst_size);
395424/// ```
396425///
426+ /// ## `#![allow(shrink)]`
427+ ///
428+ /// If `#![allow(shrink)]` is provided, `transmute_mut!` additionally supports
429+ /// transmutations that shrink the size of the referent; e.g.:
430+ ///
431+ /// ```
432+ /// # use zerocopy::transmute_mut;
433+ /// # use core::mem::size_of_val; // Not in the prelude on our MSRV
434+ /// let src: &mut [[u8; 3]] = &mut [[0, 1, 2], [3, 4, 5], [6, 7, 8]][..];
435+ /// let dst: &mut [[u8; 2]] = transmute_mut!(#![allow(shrink)] src);
436+ ///
437+ ///
438+ /// let dst_len = dst.len();
439+ /// let dst_size = size_of_val(dst);
440+ /// assert_eq!(dst, [[0, 1], [2, 3], [4, 5], [6, 7]]);
441+ ///
442+ /// assert_eq!(src.len(), 3);
443+ /// assert_eq!(dst_len, 4);
444+ ///
445+ /// assert_eq!(size_of_val(src), 9);
446+ /// assert_eq!(dst_size, 8);
447+ /// ```
448+ ///
397449/// # Errors
398450///
399451/// Violations of the alignment and size compatibility checks are detected
@@ -482,7 +534,18 @@ macro_rules! transmute_ref {
482534/// ```
483535#[ macro_export]
484536macro_rules! transmute_mut {
485- ( $e: expr) => { {
537+ ( #![ allow( shrink) ] $e: expr) => {
538+ $crate:: __transmute_mut_inner!( true , $e)
539+ } ;
540+ ( $e: expr) => {
541+ $crate:: __transmute_mut_inner!( false , $e)
542+ } ;
543+ }
544+
545+ #[ doc( hidden) ]
546+ #[ macro_export]
547+ macro_rules! __transmute_mut_inner {
548+ ( $allow_shrink: literal, $e: expr) => { {
486549 // NOTE: This must be a macro (rather than a function with trait bounds)
487550 // because, for backwards-compatibility on v0.8.x, we use the autoref
488551 // specialization trick to dispatch to different `transmute_mut`
@@ -496,7 +559,7 @@ macro_rules! transmute_mut {
496559 #[ allow( unused) ]
497560 use $crate:: util:: macro_util:: TransmuteMutDst as _;
498561 let t = $crate:: util:: macro_util:: Wrap :: new( e) ;
499- t. transmute_mut( )
562+ t. transmute_mut:: <$allow_shrink> ( )
500563 } }
501564}
502565
@@ -1244,6 +1307,11 @@ mod tests {
12441307 let slice_of_u16s: & [ U16 ] = <[ U16 ] >:: ref_from_bytes ( & [ 0 , 1 , 2 , 3 , 4 , 5 ] [ ..] ) . unwrap ( ) ;
12451308 assert_eq ! ( x, slice_of_u16s) ;
12461309
1310+ // Test that transmuting from a larger sized type to a smaller sized
1311+ // type works.
1312+ let x: & u8 = transmute_ref ! ( #![ allow( shrink) ] & 0u16 ) ;
1313+ assert_eq ! ( * x, 0 ) ;
1314+
12471315 // Test that transmuting from a type with larger trailing slice offset
12481316 // and larger trailing slice element works.
12491317 let bytes = & [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] [ ..] ;
@@ -1252,6 +1320,15 @@ mod tests {
12521320 let x: & SliceDst < U16 , u8 > = transmute_ref ! ( slice_dst_big) ;
12531321 assert_eq ! ( x, slice_dst_small) ;
12541322
1323+ let bytes = & [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] [ ..] ;
1324+ let slice_dst_big = SliceDst :: < [ u8 ; 4 ] , [ u8 ; 4 ] > :: ref_from_bytes ( bytes) . unwrap ( ) ;
1325+ let slice_dst_small = SliceDst :: < [ u8 ; 3 ] , [ u8 ; 3 ] > :: ref_from_bytes ( & bytes[ ..6 ] ) . unwrap ( ) ;
1326+ let x: & SliceDst < [ u8 ; 3 ] , [ u8 ; 3 ] > = transmute_ref ! (
1327+ #![ allow( shrink) ]
1328+ slice_dst_big
1329+ ) ;
1330+ assert_eq ! ( x, slice_dst_small) ;
1331+
12551332 // Test that it's legal to transmute a reference while shrinking the
12561333 // lifetime (note that `X` has the lifetime `'static`).
12571334 let x: & [ u8 ; 8 ] = transmute_ref ! ( X ) ;
@@ -1432,6 +1509,14 @@ mod tests {
14321509 let x: & mut [ i16 ] = transmute_mut ! ( array_of_u16s) ;
14331510 assert_eq ! ( x, array_of_i16s) ;
14341511
1512+ // Test that transmuting from a larger sized type to a smaller sized
1513+ // type works.
1514+ let mut large: [ u8 ; 2 ] = [ 1 , 1 ] ;
1515+ let x: & mut u8 = transmute_mut ! ( #![ allow( shrink) ] & mut large) ;
1516+ assert_eq ! ( * x, 1 ) ;
1517+ * x = 0 ;
1518+ assert_eq ! ( large, [ 0 , 1 ] ) ;
1519+
14351520 // Test that transmuting from a type with larger trailing slice offset
14361521 // and larger trailing slice element works.
14371522 let mut bytes = [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] ;
@@ -1440,6 +1525,16 @@ mod tests {
14401525 let slice_dst_small = SliceDst :: < U16 , u8 > :: mut_from_bytes ( & mut bytes[ ..] ) . unwrap ( ) ;
14411526 let x: & mut SliceDst < U16 , u8 > = transmute_mut ! ( slice_dst_big) ;
14421527 assert_eq ! ( x, slice_dst_small) ;
1528+
1529+ let mut bytes = [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] ;
1530+ let slice_dst_big = SliceDst :: < [ u8 ; 4 ] , [ u8 ; 4 ] > :: mut_from_bytes ( & mut bytes[ ..] ) . unwrap ( ) ;
1531+ let mut bytes = [ 0 , 1 , 2 , 3 , 4 , 5 ] ;
1532+ let slice_dst_small = SliceDst :: < [ u8 ; 3 ] , [ u8 ; 3 ] > :: mut_from_bytes ( & mut bytes[ ..] ) . unwrap ( ) ;
1533+ let x: & mut SliceDst < [ u8 ; 3 ] , [ u8 ; 3 ] > = transmute_mut ! (
1534+ #![ allow( shrink) ]
1535+ slice_dst_big
1536+ ) ;
1537+ assert_eq ! ( x, slice_dst_small) ;
14431538 }
14441539
14451540 #[ test]
0 commit comments