@@ -209,6 +209,24 @@ macro_rules! transmute {
209
209
/// assert_eq!(size_of_val(src), size_of_val(dst));
210
210
/// ```
211
211
///
212
+ /// ## `#![allow(shrink)]`
213
+ ///
214
+ /// If `#![allow(shrink)]` is provided, `transmute_ref!` additionally supports
215
+ /// transmutations that shrink the size of the referent; e.g.:
216
+ ///
217
+ /// ```
218
+ /// # use zerocopy::transmute_ref;
219
+ /// # use core::mem::size_of_val; // Not in the prelude on our MSRV
220
+ /// let src: &[[u8; 3]] = &[[0, 1, 2], [3, 4, 5], [6, 7, 8]][..];
221
+ /// let dst: &[[u8; 2]] = transmute_ref!(#![allow(shrink)] src);
222
+ ///
223
+ /// assert_eq!(src.len(), 3);
224
+ /// assert_eq!(dst.len(), 4);
225
+ /// assert_eq!(size_of_val(src), 9);
226
+ /// assert_eq!(size_of_val(dst), 8);
227
+ /// assert_eq!(dst, [[0, 1], [2, 3], [4, 5], [6, 7]]);
228
+ /// ```
229
+ ///
212
230
/// # Errors
213
231
///
214
232
/// Violations of the alignment and size compatibility checks are detected
@@ -295,7 +313,18 @@ macro_rules! transmute {
295
313
/// `Dst: Sized`.
296
314
#[ macro_export]
297
315
macro_rules! transmute_ref {
298
- ( $e: expr) => { {
316
+ ( #![ allow( shrink) ] $e: expr) => {
317
+ $crate:: __transmute_ref_inner!( true , $e)
318
+ } ;
319
+ ( $e: expr) => {
320
+ $crate:: __transmute_ref_inner!( false , $e)
321
+ } ;
322
+ }
323
+
324
+ #[ macro_export]
325
+ #[ doc( hidden) ]
326
+ macro_rules! __transmute_ref_inner {
327
+ ( $allow_shrink: literal, $e: expr) => { {
299
328
// NOTE: This must be a macro (rather than a function with trait bounds)
300
329
// because there's no way, in a generic context, to enforce that two
301
330
// types have the same size or alignment.
@@ -334,10 +363,10 @@ macro_rules! transmute_ref {
334
363
// - `Src: IntoBytes + Immutable`
335
364
// - `Dst: FromBytes + Immutable`
336
365
unsafe {
337
- t. transmute_ref( )
366
+ t. transmute_ref:: <$allow_shrink> ( )
338
367
}
339
368
}
340
- } }
369
+ } } ;
341
370
}
342
371
343
372
/// Safely transmutes a mutable reference of one type to a mutable reference of
@@ -383,6 +412,29 @@ macro_rules! transmute_ref {
383
412
/// assert_eq!(size_of_val(src), dst_size);
384
413
/// ```
385
414
///
415
+ /// ## `#![allow(shrink)]`
416
+ ///
417
+ /// If `#![allow(shrink)]` is provided, `transmute_mut!` additionally supports
418
+ /// transmutations that shrink the size of the referent; e.g.:
419
+ ///
420
+ /// ```
421
+ /// # use zerocopy::transmute_mut;
422
+ /// # use core::mem::size_of_val; // Not in the prelude on our MSRV
423
+ /// let src: &mut [[u8; 3]] = &mut [[0, 1, 2], [3, 4, 5], [6, 7, 8]][..];
424
+ /// let dst: &mut [[u8; 2]] = transmute_mut!(#![allow(shrink)] src);
425
+ ///
426
+ ///
427
+ /// let dst_len = dst.len();
428
+ /// let dst_size = size_of_val(dst);
429
+ /// assert_eq!(dst, [[0, 1], [2, 3], [4, 5], [6, 7]]);
430
+ ///
431
+ /// assert_eq!(src.len(), 3);
432
+ /// assert_eq!(dst_len, 4);
433
+ ///
434
+ /// assert_eq!(size_of_val(src), 9);
435
+ /// assert_eq!(dst_size, 8);
436
+ /// ```
437
+ ///
386
438
/// # Errors
387
439
///
388
440
/// Violations of the alignment and size compatibility checks are detected
@@ -471,7 +523,18 @@ macro_rules! transmute_ref {
471
523
/// ```
472
524
#[ macro_export]
473
525
macro_rules! transmute_mut {
474
- ( $e: expr) => { {
526
+ ( #![ allow( shrink) ] $e: expr) => {
527
+ $crate:: __transmute_mut_inner!( true , $e)
528
+ } ;
529
+ ( $e: expr) => {
530
+ $crate:: __transmute_mut_inner!( false , $e)
531
+ } ;
532
+ }
533
+
534
+ #[ doc( hidden) ]
535
+ #[ macro_export]
536
+ macro_rules! __transmute_mut_inner {
537
+ ( $allow_shrink: literal, $e: expr) => { {
475
538
// NOTE: This must be a macro (rather than a function with trait bounds)
476
539
// because, for backwards-compatibility on v0.8.x, we use the autoref
477
540
// specialization trick to dispatch to different `transmute_mut`
@@ -485,7 +548,7 @@ macro_rules! transmute_mut {
485
548
#[ allow( unused) ]
486
549
use $crate:: util:: macro_util:: TransmuteMutDst as _;
487
550
let t = $crate:: util:: macro_util:: Wrap :: new( e) ;
488
- t. transmute_mut( )
551
+ t. transmute_mut:: <$allow_shrink> ( )
489
552
} }
490
553
}
491
554
@@ -1233,6 +1296,11 @@ mod tests {
1233
1296
let slice_of_u16s: & [ U16 ] = <[ U16 ] >:: ref_from_bytes ( & [ 0 , 1 , 2 , 3 , 4 , 5 ] [ ..] ) . unwrap ( ) ;
1234
1297
assert_eq ! ( x, slice_of_u16s) ;
1235
1298
1299
+ // Test that transmuting from a larger sized type to a smaller sized
1300
+ // type works.
1301
+ let x: & u8 = transmute_ref ! ( #![ allow( shrink) ] & 0u16 ) ;
1302
+ assert_eq ! ( * x, 0 ) ;
1303
+
1236
1304
// Test that transmuting from a type with larger trailing slice offset
1237
1305
// and larger trailing slice element works.
1238
1306
let bytes = & [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] [ ..] ;
@@ -1241,6 +1309,15 @@ mod tests {
1241
1309
let x: & SliceDst < U16 , u8 > = transmute_ref ! ( slice_dst_big) ;
1242
1310
assert_eq ! ( x, slice_dst_small) ;
1243
1311
1312
+ let bytes = & [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] [ ..] ;
1313
+ let slice_dst_big = SliceDst :: < [ u8 ; 4 ] , [ u8 ; 4 ] > :: ref_from_bytes ( bytes) . unwrap ( ) ;
1314
+ let slice_dst_small = SliceDst :: < [ u8 ; 3 ] , [ u8 ; 3 ] > :: ref_from_bytes ( & bytes[ ..6 ] ) . unwrap ( ) ;
1315
+ let x: & SliceDst < [ u8 ; 3 ] , [ u8 ; 3 ] > = transmute_ref ! (
1316
+ #![ allow( shrink) ]
1317
+ slice_dst_big
1318
+ ) ;
1319
+ assert_eq ! ( x, slice_dst_small) ;
1320
+
1244
1321
// Test that it's legal to transmute a reference while shrinking the
1245
1322
// lifetime (note that `X` has the lifetime `'static`).
1246
1323
let x: & [ u8 ; 8 ] = transmute_ref ! ( X ) ;
@@ -1421,6 +1498,14 @@ mod tests {
1421
1498
let x: & mut [ i16 ] = transmute_mut ! ( array_of_u16s) ;
1422
1499
assert_eq ! ( x, array_of_i16s) ;
1423
1500
1501
+ // Test that transmuting from a larger sized type to a smaller sized
1502
+ // type works.
1503
+ let mut large: [ u8 ; 2 ] = [ 1 , 1 ] ;
1504
+ let x: & mut u8 = transmute_mut ! ( #![ allow( shrink) ] & mut large) ;
1505
+ assert_eq ! ( * x, 1 ) ;
1506
+ * x = 0 ;
1507
+ assert_eq ! ( large, [ 0 , 1 ] ) ;
1508
+
1424
1509
// Test that transmuting from a type with larger trailing slice offset
1425
1510
// and larger trailing slice element works.
1426
1511
let mut bytes = [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] ;
@@ -1429,6 +1514,16 @@ mod tests {
1429
1514
let slice_dst_small = SliceDst :: < U16 , u8 > :: mut_from_bytes ( & mut bytes[ ..] ) . unwrap ( ) ;
1430
1515
let x: & mut SliceDst < U16 , u8 > = transmute_mut ! ( slice_dst_big) ;
1431
1516
assert_eq ! ( x, slice_dst_small) ;
1517
+
1518
+ let mut bytes = [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] ;
1519
+ let slice_dst_big = SliceDst :: < [ u8 ; 4 ] , [ u8 ; 4 ] > :: mut_from_bytes ( & mut bytes[ ..] ) . unwrap ( ) ;
1520
+ let mut bytes = [ 0 , 1 , 2 , 3 , 4 , 5 ] ;
1521
+ let slice_dst_small = SliceDst :: < [ u8 ; 3 ] , [ u8 ; 3 ] > :: mut_from_bytes ( & mut bytes[ ..] ) . unwrap ( ) ;
1522
+ let x: & mut SliceDst < [ u8 ; 3 ] , [ u8 ; 3 ] > = transmute_mut ! (
1523
+ #![ allow( shrink) ]
1524
+ slice_dst_big
1525
+ ) ;
1526
+ assert_eq ! ( x, slice_dst_small) ;
1432
1527
}
1433
1528
1434
1529
#[ test]
0 commit comments