@@ -138,6 +138,24 @@ macro_rules! transmute {
138138/// assert_eq!(size_of_val(src), size_of_val(dst));
139139/// ```
140140///
141+ /// ## `#![allow(shrink)]`
142+ ///
143+ /// If `#![allow(shrink)]` is provided, `transmute_ref!` additionally supports
144+ /// transmutations that shrink the size of the referent; e.g.:
145+ ///
146+ /// ```
147+ /// # use zerocopy::transmute_ref;
148+ /// # use core::mem::size_of_val; // Not in the prelude on our MSRV
149+ /// let src: &[[u8; 3]] = &[[0, 1, 2], [3, 4, 5], [6, 7, 8]][..];
150+ /// let dst: &[[u8; 2]] = transmute_ref!(#![allow(shrink)] src);
151+ ///
152+ /// assert_eq!(src.len(), 3);
153+ /// assert_eq!(dst.len(), 4);
154+ /// assert_eq!(size_of_val(src), 9);
155+ /// assert_eq!(size_of_val(dst), 8);
156+ /// assert_eq!(dst, [[0, 1], [2, 3], [4, 5], [6, 7]]);
157+ /// ```
158+ ///
141159/// # Errors
142160///
143161/// Violations of the alignment and size compatibility checks are detected
@@ -224,7 +242,18 @@ macro_rules! transmute {
224242/// `Dst: Sized`.
225243#[ macro_export]
226244macro_rules! transmute_ref {
227- ( $e: expr) => { {
245+ ( #![ allow( shrink) ] $e: expr) => {
246+ $crate:: __transmute_ref_inner!( true , $e)
247+ } ;
248+ ( $e: expr) => {
249+ $crate:: __transmute_ref_inner!( false , $e)
250+ } ;
251+ }
252+
253+ #[ macro_export]
254+ #[ doc( hidden) ]
255+ macro_rules! __transmute_ref_inner {
256+ ( $allow_shrink: literal, $e: expr) => { {
228257 // NOTE: This must be a macro (rather than a function with trait bounds)
229258 // because there's no way, in a generic context, to enforce that two
230259 // types have the same size or alignment.
@@ -263,10 +292,10 @@ macro_rules! transmute_ref {
263292 // - `Src: IntoBytes + Immutable`
264293 // - `Dst: FromBytes + Immutable`
265294 unsafe {
266- t. transmute_ref( )
295+ t. transmute_ref:: <$allow_shrink> ( )
267296 }
268297 }
269- } }
298+ } } ;
270299}
271300
272301/// Safely transmutes a mutable reference of one type to a mutable reference of
@@ -312,6 +341,29 @@ macro_rules! transmute_ref {
312341/// assert_eq!(size_of_val(src), dst_size);
313342/// ```
314343///
344+ /// ## `#![allow(shrink)]`
345+ ///
346+ /// If `#![allow(shrink)]` is provided, `transmute_mut!` additionally supports
347+ /// transmutations that shrink the size of the referent; e.g.:
348+ ///
349+ /// ```
350+ /// # use zerocopy::transmute_mut;
351+ /// # use core::mem::size_of_val; // Not in the prelude on our MSRV
352+ /// let src: &mut [[u8; 3]] = &mut [[0, 1, 2], [3, 4, 5], [6, 7, 8]][..];
353+ /// let dst: &mut [[u8; 2]] = transmute_mut!(#![allow(shrink)] src);
354+ ///
355+ ///
356+ /// let dst_len = dst.len();
357+ /// let dst_size = size_of_val(dst);
358+ /// assert_eq!(dst, [[0, 1], [2, 3], [4, 5], [6, 7]]);
359+ ///
360+ /// assert_eq!(src.len(), 3);
361+ /// assert_eq!(dst_len, 4);
362+ ///
363+ /// assert_eq!(size_of_val(src), 9);
364+ /// assert_eq!(dst_size, 8);
365+ /// ```
366+ ///
315367/// # Errors
316368///
317369/// Violations of the alignment and size compatibility checks are detected
@@ -400,7 +452,18 @@ macro_rules! transmute_ref {
400452/// ```
401453#[ macro_export]
402454macro_rules! transmute_mut {
403- ( $e: expr) => { {
455+ ( #![ allow( shrink) ] $e: expr) => {
456+ $crate:: __transmute_mut_inner!( true , $e)
457+ } ;
458+ ( $e: expr) => {
459+ $crate:: __transmute_mut_inner!( false , $e)
460+ } ;
461+ }
462+
463+ #[ doc( hidden) ]
464+ #[ macro_export]
465+ macro_rules! __transmute_mut_inner {
466+ ( $allow_shrink: literal, $e: expr) => { {
404467 // NOTE: This must be a macro (rather than a function with trait bounds)
405468 // because, for backwards-compatibility on v0.8.x, we use the autoref
406469 // specialization trick to dispatch to different `transmute_mut`
@@ -414,7 +477,7 @@ macro_rules! transmute_mut {
414477 #[ allow( unused) ]
415478 use $crate:: util:: macro_util:: TransmuteMutDst as _;
416479 let t = $crate:: util:: macro_util:: Wrap :: new( e) ;
417- t. transmute_mut( )
480+ t. transmute_mut:: <$allow_shrink> ( )
418481 } }
419482}
420483
@@ -1154,6 +1217,11 @@ mod tests {
11541217 let slice_of_u16s: & [ U16 ] = <[ U16 ] >:: ref_from_bytes ( & [ 0 , 1 , 2 , 3 , 4 , 5 ] [ ..] ) . unwrap ( ) ;
11551218 assert_eq ! ( x, slice_of_u16s) ;
11561219
1220+ // Test that transmuting from a larger sized type to a smaller sized
1221+ // type works.
1222+ let x: & u8 = transmute_ref ! ( #![ allow( shrink) ] & 0u16 ) ;
1223+ assert_eq ! ( * x, 0 ) ;
1224+
11571225 // Test that transmuting from a type with larger trailing slice offset
11581226 // and larger trailing slice element works.
11591227 let bytes = & [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] [ ..] ;
@@ -1162,6 +1230,15 @@ mod tests {
11621230 let x: & SliceDst < U16 , u8 > = transmute_ref ! ( slice_dst_big) ;
11631231 assert_eq ! ( x, slice_dst_small) ;
11641232
1233+ let bytes = & [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] [ ..] ;
1234+ let slice_dst_big = SliceDst :: < [ u8 ; 4 ] , [ u8 ; 4 ] > :: ref_from_bytes ( bytes) . unwrap ( ) ;
1235+ let slice_dst_small = SliceDst :: < [ u8 ; 3 ] , [ u8 ; 3 ] > :: ref_from_bytes ( & bytes[ ..6 ] ) . unwrap ( ) ;
1236+ let x: & SliceDst < [ u8 ; 3 ] , [ u8 ; 3 ] > = transmute_ref ! (
1237+ #![ allow( shrink) ]
1238+ slice_dst_big
1239+ ) ;
1240+ assert_eq ! ( x, slice_dst_small) ;
1241+
11651242 // Test that it's legal to transmute a reference while shrinking the
11661243 // lifetime (note that `X` has the lifetime `'static`).
11671244 let x: & [ u8 ; 8 ] = transmute_ref ! ( X ) ;
@@ -1342,6 +1419,14 @@ mod tests {
13421419 let x: & mut [ i16 ] = transmute_mut ! ( array_of_u16s) ;
13431420 assert_eq ! ( x, array_of_i16s) ;
13441421
1422+ // Test that transmuting from a larger sized type to a smaller sized
1423+ // type works.
1424+ let mut large: [ u8 ; 2 ] = [ 1 , 1 ] ;
1425+ let x: & mut u8 = transmute_mut ! ( #![ allow( shrink) ] & mut large) ;
1426+ assert_eq ! ( * x, 1 ) ;
1427+ * x = 0 ;
1428+ assert_eq ! ( large, [ 0 , 1 ] ) ;
1429+
13451430 // Test that transmuting from a type with larger trailing slice offset
13461431 // and larger trailing slice element works.
13471432 let mut bytes = [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] ;
@@ -1350,6 +1435,16 @@ mod tests {
13501435 let slice_dst_small = SliceDst :: < U16 , u8 > :: mut_from_bytes ( & mut bytes[ ..] ) . unwrap ( ) ;
13511436 let x: & mut SliceDst < U16 , u8 > = transmute_mut ! ( slice_dst_big) ;
13521437 assert_eq ! ( x, slice_dst_small) ;
1438+
1439+ let mut bytes = [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] ;
1440+ let slice_dst_big = SliceDst :: < [ u8 ; 4 ] , [ u8 ; 4 ] > :: mut_from_bytes ( & mut bytes[ ..] ) . unwrap ( ) ;
1441+ let mut bytes = [ 0 , 1 , 2 , 3 , 4 , 5 ] ;
1442+ let slice_dst_small = SliceDst :: < [ u8 ; 3 ] , [ u8 ; 3 ] > :: mut_from_bytes ( & mut bytes[ ..] ) . unwrap ( ) ;
1443+ let x: & mut SliceDst < [ u8 ; 3 ] , [ u8 ; 3 ] > = transmute_mut ! (
1444+ #![ allow( shrink) ]
1445+ slice_dst_big
1446+ ) ;
1447+ assert_eq ! ( x, slice_dst_small) ;
13531448 }
13541449
13551450 #[ test]
0 commit comments