@@ -208,6 +208,24 @@ macro_rules! transmute {
208208/// assert_eq!(size_of_val(src), size_of_val(dst));
209209/// ```
210210///
211+ /// ## `#![allow(shrink)]`
212+ ///
213+ /// If `#![allow(shrink)]` is provided, `transmute_ref!` additionally supports
214+ /// transmutations that shrink the size of the referent; e.g.:
215+ ///
216+ /// ```
217+ /// # use zerocopy::transmute_ref;
218+ /// # use core::mem::size_of_val; // Not in the prelude on our MSRV
219+ /// let src: &[[u8; 3]] = &[[0, 1, 2], [3, 4, 5], [6, 7, 8]][..];
220+ /// let dst: &[[u8; 2]] = transmute_ref!(#![allow(shrink)] src);
221+ ///
222+ /// assert_eq!(src.len(), 3);
223+ /// assert_eq!(dst.len(), 4);
224+ /// assert_eq!(size_of_val(src), 9);
225+ /// assert_eq!(size_of_val(dst), 8);
226+ /// assert_eq!(dst, [[0, 1], [2, 3], [4, 5], [6, 7]]);
227+ /// ```
228+ ///
211229/// # Errors
212230///
213231/// Violations of the alignment and size compatibility checks are detected
@@ -294,7 +312,18 @@ macro_rules! transmute {
294312/// `Dst: Sized`.
295313#[ macro_export]
296314macro_rules! transmute_ref {
297- ( $e: expr) => { {
315+ ( #![ allow( shrink) ] $e: expr) => {
316+ $crate:: __transmute_ref_inner!( true , $e)
317+ } ;
318+ ( $e: expr) => {
319+ $crate:: __transmute_ref_inner!( false , $e)
320+ } ;
321+ }
322+
323+ #[ macro_export]
324+ #[ doc( hidden) ]
325+ macro_rules! __transmute_ref_inner {
326+ ( $allow_shrink: literal, $e: expr) => { {
298327 // NOTE: This must be a macro (rather than a function with trait bounds)
299328 // because there's no way, in a generic context, to enforce that two
300329 // types have the same size or alignment.
@@ -333,10 +362,10 @@ macro_rules! transmute_ref {
333362 // - `Src: IntoBytes + Immutable`
334363 // - `Dst: FromBytes + Immutable`
335364 unsafe {
336- t. transmute_ref( )
365+ t. transmute_ref:: <$allow_shrink> ( )
337366 }
338367 }
339- } }
368+ } } ;
340369}
341370
342371/// Safely transmutes a mutable reference of one type to a mutable reference of
@@ -382,6 +411,29 @@ macro_rules! transmute_ref {
382411/// assert_eq!(size_of_val(src), dst_size);
383412/// ```
384413///
414+ /// ## `#![allow(shrink)]`
415+ ///
416+ /// If `#![allow(shrink)]` is provided, `transmute_mut!` additionally supports
417+ /// transmutations that shrink the size of the referent; e.g.:
418+ ///
419+ /// ```
420+ /// # use zerocopy::transmute_mut;
421+ /// # use core::mem::size_of_val; // Not in the prelude on our MSRV
422+ /// let src: &mut [[u8; 3]] = &mut [[0, 1, 2], [3, 4, 5], [6, 7, 8]][..];
423+ /// let dst: &mut [[u8; 2]] = transmute_mut!(#![allow(shrink)] src);
424+ ///
425+ ///
426+ /// let dst_len = dst.len();
427+ /// let dst_size = size_of_val(dst);
428+ /// assert_eq!(dst, [[0, 1], [2, 3], [4, 5], [6, 7]]);
429+ ///
430+ /// assert_eq!(src.len(), 3);
431+ /// assert_eq!(dst_len, 4);
432+ ///
433+ /// assert_eq!(size_of_val(src), 9);
434+ /// assert_eq!(dst_size, 8);
435+ /// ```
436+ ///
385437/// # Errors
386438///
387439/// Violations of the alignment and size compatibility checks are detected
@@ -470,7 +522,18 @@ macro_rules! transmute_ref {
470522/// ```
471523#[ macro_export]
472524macro_rules! transmute_mut {
473- ( $e: expr) => { {
525+ ( #![ allow( shrink) ] $e: expr) => {
526+ $crate:: __transmute_mut_inner!( true , $e)
527+ } ;
528+ ( $e: expr) => {
529+ $crate:: __transmute_mut_inner!( false , $e)
530+ } ;
531+ }
532+
533+ #[ doc( hidden) ]
534+ #[ macro_export]
535+ macro_rules! __transmute_mut_inner {
536+ ( $allow_shrink: literal, $e: expr) => { {
474537 // NOTE: This must be a macro (rather than a function with trait bounds)
475538 // because, for backwards-compatibility on v0.8.x, we use the autoref
476539 // specialization trick to dispatch to different `transmute_mut`
@@ -484,7 +547,7 @@ macro_rules! transmute_mut {
484547 #[ allow( unused) ]
485548 use $crate:: util:: macro_util:: TransmuteMutDst as _;
486549 let t = $crate:: util:: macro_util:: Wrap :: new( e) ;
487- t. transmute_mut( )
550+ t. transmute_mut:: <$allow_shrink> ( )
488551 } }
489552}
490553
@@ -1232,6 +1295,11 @@ mod tests {
12321295 let slice_of_u16s: & [ U16 ] = <[ U16 ] >:: ref_from_bytes ( & [ 0 , 1 , 2 , 3 , 4 , 5 ] [ ..] ) . unwrap ( ) ;
12331296 assert_eq ! ( x, slice_of_u16s) ;
12341297
1298+ // Test that transmuting from a larger sized type to a smaller sized
1299+ // type works.
1300+ let x: & u8 = transmute_ref ! ( #![ allow( shrink) ] & 0u16 ) ;
1301+ assert_eq ! ( * x, 0 ) ;
1302+
12351303 // Test that transmuting from a type with larger trailing slice offset
12361304 // and larger trailing slice element works.
12371305 let bytes = & [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] [ ..] ;
@@ -1240,6 +1308,15 @@ mod tests {
12401308 let x: & SliceDst < U16 , u8 > = transmute_ref ! ( slice_dst_big) ;
12411309 assert_eq ! ( x, slice_dst_small) ;
12421310
1311+ let bytes = & [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] [ ..] ;
1312+ let slice_dst_big = SliceDst :: < [ u8 ; 4 ] , [ u8 ; 4 ] > :: ref_from_bytes ( bytes) . unwrap ( ) ;
1313+ let slice_dst_small = SliceDst :: < [ u8 ; 3 ] , [ u8 ; 3 ] > :: ref_from_bytes ( & bytes[ ..6 ] ) . unwrap ( ) ;
1314+ let x: & SliceDst < [ u8 ; 3 ] , [ u8 ; 3 ] > = transmute_ref ! (
1315+ #![ allow( shrink) ]
1316+ slice_dst_big
1317+ ) ;
1318+ assert_eq ! ( x, slice_dst_small) ;
1319+
12431320 // Test that it's legal to transmute a reference while shrinking the
12441321 // lifetime (note that `X` has the lifetime `'static`).
12451322 let x: & [ u8 ; 8 ] = transmute_ref ! ( X ) ;
@@ -1420,6 +1497,14 @@ mod tests {
14201497 let x: & mut [ i16 ] = transmute_mut ! ( array_of_u16s) ;
14211498 assert_eq ! ( x, array_of_i16s) ;
14221499
1500+ // Test that transmuting from a larger sized type to a smaller sized
1501+ // type works.
1502+ let mut large: [ u8 ; 2 ] = [ 1 , 1 ] ;
1503+ let x: & mut u8 = transmute_mut ! ( #![ allow( shrink) ] & mut large) ;
1504+ assert_eq ! ( * x, 1 ) ;
1505+ * x = 0 ;
1506+ assert_eq ! ( large, [ 0 , 1 ] ) ;
1507+
14231508 // Test that transmuting from a type with larger trailing slice offset
14241509 // and larger trailing slice element works.
14251510 let mut bytes = [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] ;
@@ -1428,6 +1513,16 @@ mod tests {
14281513 let slice_dst_small = SliceDst :: < U16 , u8 > :: mut_from_bytes ( & mut bytes[ ..] ) . unwrap ( ) ;
14291514 let x: & mut SliceDst < U16 , u8 > = transmute_mut ! ( slice_dst_big) ;
14301515 assert_eq ! ( x, slice_dst_small) ;
1516+
1517+ let mut bytes = [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] ;
1518+ let slice_dst_big = SliceDst :: < [ u8 ; 4 ] , [ u8 ; 4 ] > :: mut_from_bytes ( & mut bytes[ ..] ) . unwrap ( ) ;
1519+ let mut bytes = [ 0 , 1 , 2 , 3 , 4 , 5 ] ;
1520+ let slice_dst_small = SliceDst :: < [ u8 ; 3 ] , [ u8 ; 3 ] > :: mut_from_bytes ( & mut bytes[ ..] ) . unwrap ( ) ;
1521+ let x: & mut SliceDst < [ u8 ; 3 ] , [ u8 ; 3 ] > = transmute_mut ! (
1522+ #![ allow( shrink) ]
1523+ slice_dst_big
1524+ ) ;
1525+ assert_eq ! ( x, slice_dst_small) ;
14311526 }
14321527
14331528 #[ test]
0 commit comments