@@ -219,6 +219,24 @@ macro_rules! transmute {
219219/// assert_eq!(size_of_val(src), size_of_val(dst));
220220/// ```
221221///
222+ /// ## `#![allow(shrink)]`
223+ ///
224+ /// If `#![allow(shrink)]` is provided, `transmute_ref!` additionally supports
225+ /// transmutations that shrink the size of the referent; e.g.:
226+ ///
227+ /// ```
228+ /// # use zerocopy::transmute_ref;
229+ /// # use core::mem::size_of_val; // Not in the prelude on our MSRV
230+ /// let src: &[[u8; 3]] = &[[0, 1, 2], [3, 4, 5], [6, 7, 8]][..];
231+ /// let dst: &[[u8; 2]] = transmute_ref!(#![allow(shrink)] src);
232+ ///
233+ /// assert_eq!(src.len(), 3);
234+ /// assert_eq!(dst.len(), 4);
235+ /// assert_eq!(size_of_val(src), 9);
236+ /// assert_eq!(size_of_val(dst), 8);
237+ /// assert_eq!(dst, [[0, 1], [2, 3], [4, 5], [6, 7]]);
238+ /// ```
239+ ///
222240/// # Errors
223241///
224242/// Violations of the alignment and size compatibility checks are detected
@@ -305,7 +323,18 @@ macro_rules! transmute {
305323/// `Dst: Sized`.
306324#[ macro_export]
307325macro_rules! transmute_ref {
308- ( $e: expr) => { {
326+ ( #![ allow( shrink) ] $e: expr) => {
327+ $crate:: __transmute_ref_inner!( true , $e)
328+ } ;
329+ ( $e: expr) => {
330+ $crate:: __transmute_ref_inner!( false , $e)
331+ } ;
332+ }
333+
334+ #[ macro_export]
335+ #[ doc( hidden) ]
336+ macro_rules! __transmute_ref_inner {
337+ ( $allow_shrink: literal, $e: expr) => { {
309338 // NOTE: This must be a macro (rather than a function with trait bounds)
310339 // because there's no way, in a generic context, to enforce that two
311340 // types have the same size or alignment.
@@ -344,10 +373,10 @@ macro_rules! transmute_ref {
344373 // - `Src: IntoBytes + Immutable`
345374 // - `Dst: FromBytes + Immutable`
346375 unsafe {
347- t. transmute_ref( )
376+ t. transmute_ref:: <$allow_shrink> ( )
348377 }
349378 }
350- } }
379+ } } ;
351380}
352381
353382/// Safely transmutes a mutable reference of one type to a mutable reference of
@@ -393,6 +422,29 @@ macro_rules! transmute_ref {
393422/// assert_eq!(size_of_val(src), dst_size);
394423/// ```
395424///
425+ /// ## `#![allow(shrink)]`
426+ ///
427+ /// If `#![allow(shrink)]` is provided, `transmute_mut!` additionally supports
428+ /// transmutations that shrink the size of the referent; e.g.:
429+ ///
430+ /// ```
431+ /// # use zerocopy::transmute_mut;
432+ /// # use core::mem::size_of_val; // Not in the prelude on our MSRV
433+ /// let src: &mut [[u8; 3]] = &mut [[0, 1, 2], [3, 4, 5], [6, 7, 8]][..];
434+ /// let dst: &mut [[u8; 2]] = transmute_mut!(#![allow(shrink)] src);
435+ ///
436+ ///
437+ /// let dst_len = dst.len();
438+ /// let dst_size = size_of_val(dst);
439+ /// assert_eq!(dst, [[0, 1], [2, 3], [4, 5], [6, 7]]);
440+ ///
441+ /// assert_eq!(src.len(), 3);
442+ /// assert_eq!(dst_len, 4);
443+ ///
444+ /// assert_eq!(size_of_val(src), 9);
445+ /// assert_eq!(dst_size, 8);
446+ /// ```
447+ ///
396448/// # Errors
397449///
398450/// Violations of the alignment and size compatibility checks are detected
@@ -481,7 +533,18 @@ macro_rules! transmute_ref {
481533/// ```
482534#[ macro_export]
483535macro_rules! transmute_mut {
484- ( $e: expr) => { {
536+ ( #![ allow( shrink) ] $e: expr) => {
537+ $crate:: __transmute_mut_inner!( true , $e)
538+ } ;
539+ ( $e: expr) => {
540+ $crate:: __transmute_mut_inner!( false , $e)
541+ } ;
542+ }
543+
544+ #[ doc( hidden) ]
545+ #[ macro_export]
546+ macro_rules! __transmute_mut_inner {
547+ ( $allow_shrink: literal, $e: expr) => { {
485548 // NOTE: This must be a macro (rather than a function with trait bounds)
486549 // because, for backwards-compatibility on v0.8.x, we use the autoref
487550 // specialization trick to dispatch to different `transmute_mut`
@@ -495,7 +558,7 @@ macro_rules! transmute_mut {
495558 #[ allow( unused) ]
496559 use $crate:: util:: macro_util:: TransmuteMutDst as _;
497560 let t = $crate:: util:: macro_util:: Wrap :: new( e) ;
498- t. transmute_mut( )
561+ t. transmute_mut:: <$allow_shrink> ( )
499562 } }
500563}
501564
@@ -1243,6 +1306,11 @@ mod tests {
12431306 let slice_of_u16s: & [ U16 ] = <[ U16 ] >:: ref_from_bytes ( & [ 0 , 1 , 2 , 3 , 4 , 5 ] [ ..] ) . unwrap ( ) ;
12441307 assert_eq ! ( x, slice_of_u16s) ;
12451308
1309+ // Test that transmuting from a larger sized type to a smaller sized
1310+ // type works.
1311+ let x: & u8 = transmute_ref ! ( #![ allow( shrink) ] & 0u16 ) ;
1312+ assert_eq ! ( * x, 0 ) ;
1313+
12461314 // Test that transmuting from a type with larger trailing slice offset
12471315 // and larger trailing slice element works.
12481316 let bytes = & [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] [ ..] ;
@@ -1251,6 +1319,15 @@ mod tests {
12511319 let x: & SliceDst < U16 , u8 > = transmute_ref ! ( slice_dst_big) ;
12521320 assert_eq ! ( x, slice_dst_small) ;
12531321
1322+ let bytes = & [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] [ ..] ;
1323+ let slice_dst_big = SliceDst :: < [ u8 ; 4 ] , [ u8 ; 4 ] > :: ref_from_bytes ( bytes) . unwrap ( ) ;
1324+ let slice_dst_small = SliceDst :: < [ u8 ; 3 ] , [ u8 ; 3 ] > :: ref_from_bytes ( & bytes[ ..6 ] ) . unwrap ( ) ;
1325+ let x: & SliceDst < [ u8 ; 3 ] , [ u8 ; 3 ] > = transmute_ref ! (
1326+ #![ allow( shrink) ]
1327+ slice_dst_big
1328+ ) ;
1329+ assert_eq ! ( x, slice_dst_small) ;
1330+
12541331 // Test that it's legal to transmute a reference while shrinking the
12551332 // lifetime (note that `X` has the lifetime `'static`).
12561333 let x: & [ u8 ; 8 ] = transmute_ref ! ( X ) ;
@@ -1431,6 +1508,14 @@ mod tests {
14311508 let x: & mut [ i16 ] = transmute_mut ! ( array_of_u16s) ;
14321509 assert_eq ! ( x, array_of_i16s) ;
14331510
1511+ // Test that transmuting from a larger sized type to a smaller sized
1512+ // type works.
1513+ let mut large: [ u8 ; 2 ] = [ 1 , 1 ] ;
1514+ let x: & mut u8 = transmute_mut ! ( #![ allow( shrink) ] & mut large) ;
1515+ assert_eq ! ( * x, 1 ) ;
1516+ * x = 0 ;
1517+ assert_eq ! ( large, [ 0 , 1 ] ) ;
1518+
14341519 // Test that transmuting from a type with larger trailing slice offset
14351520 // and larger trailing slice element works.
14361521 let mut bytes = [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] ;
@@ -1439,6 +1524,16 @@ mod tests {
14391524 let slice_dst_small = SliceDst :: < U16 , u8 > :: mut_from_bytes ( & mut bytes[ ..] ) . unwrap ( ) ;
14401525 let x: & mut SliceDst < U16 , u8 > = transmute_mut ! ( slice_dst_big) ;
14411526 assert_eq ! ( x, slice_dst_small) ;
1527+
1528+ let mut bytes = [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] ;
1529+ let slice_dst_big = SliceDst :: < [ u8 ; 4 ] , [ u8 ; 4 ] > :: mut_from_bytes ( & mut bytes[ ..] ) . unwrap ( ) ;
1530+ let mut bytes = [ 0 , 1 , 2 , 3 , 4 , 5 ] ;
1531+ let slice_dst_small = SliceDst :: < [ u8 ; 3 ] , [ u8 ; 3 ] > :: mut_from_bytes ( & mut bytes[ ..] ) . unwrap ( ) ;
1532+ let x: & mut SliceDst < [ u8 ; 3 ] , [ u8 ; 3 ] > = transmute_mut ! (
1533+ #![ allow( shrink) ]
1534+ slice_dst_big
1535+ ) ;
1536+ assert_eq ! ( x, slice_dst_small) ;
14421537 }
14431538
14441539 #[ test]
0 commit comments