@@ -220,6 +220,24 @@ macro_rules! transmute {
220
220
/// assert_eq!(size_of_val(src), size_of_val(dst));
221
221
/// ```
222
222
///
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
+ ///
223
241
/// # Errors
224
242
///
225
243
/// Violations of the alignment and size compatibility checks are detected
@@ -306,7 +324,18 @@ macro_rules! transmute {
306
324
/// `Dst: Sized`.
307
325
#[ macro_export]
308
326
macro_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) => { {
310
339
// NOTE: This must be a macro (rather than a function with trait bounds)
311
340
// because there's no way, in a generic context, to enforce that two
312
341
// types have the same size or alignment.
@@ -345,10 +374,10 @@ macro_rules! transmute_ref {
345
374
// - `Src: IntoBytes + Immutable`
346
375
// - `Dst: FromBytes + Immutable`
347
376
unsafe {
348
- t. transmute_ref( )
377
+ t. transmute_ref:: <$allow_shrink> ( )
349
378
}
350
379
}
351
- } }
380
+ } } ;
352
381
}
353
382
354
383
/// Safely transmutes a mutable reference of one type to a mutable reference of
@@ -394,6 +423,29 @@ macro_rules! transmute_ref {
394
423
/// assert_eq!(size_of_val(src), dst_size);
395
424
/// ```
396
425
///
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
+ ///
397
449
/// # Errors
398
450
///
399
451
/// Violations of the alignment and size compatibility checks are detected
@@ -482,7 +534,18 @@ macro_rules! transmute_ref {
482
534
/// ```
483
535
#[ macro_export]
484
536
macro_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) => { {
486
549
// NOTE: This must be a macro (rather than a function with trait bounds)
487
550
// because, for backwards-compatibility on v0.8.x, we use the autoref
488
551
// specialization trick to dispatch to different `transmute_mut`
@@ -496,7 +559,7 @@ macro_rules! transmute_mut {
496
559
#[ allow( unused) ]
497
560
use $crate:: util:: macro_util:: TransmuteMutDst as _;
498
561
let t = $crate:: util:: macro_util:: Wrap :: new( e) ;
499
- t. transmute_mut( )
562
+ t. transmute_mut:: <$allow_shrink> ( )
500
563
} }
501
564
}
502
565
@@ -1244,6 +1307,11 @@ mod tests {
1244
1307
let slice_of_u16s: & [ U16 ] = <[ U16 ] >:: ref_from_bytes ( & [ 0 , 1 , 2 , 3 , 4 , 5 ] [ ..] ) . unwrap ( ) ;
1245
1308
assert_eq ! ( x, slice_of_u16s) ;
1246
1309
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
+
1247
1315
// Test that transmuting from a type with larger trailing slice offset
1248
1316
// and larger trailing slice element works.
1249
1317
let bytes = & [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] [ ..] ;
@@ -1252,6 +1320,15 @@ mod tests {
1252
1320
let x: & SliceDst < U16 , u8 > = transmute_ref ! ( slice_dst_big) ;
1253
1321
assert_eq ! ( x, slice_dst_small) ;
1254
1322
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
+
1255
1332
// Test that it's legal to transmute a reference while shrinking the
1256
1333
// lifetime (note that `X` has the lifetime `'static`).
1257
1334
let x: & [ u8 ; 8 ] = transmute_ref ! ( X ) ;
@@ -1432,6 +1509,14 @@ mod tests {
1432
1509
let x: & mut [ i16 ] = transmute_mut ! ( array_of_u16s) ;
1433
1510
assert_eq ! ( x, array_of_i16s) ;
1434
1511
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
+
1435
1520
// Test that transmuting from a type with larger trailing slice offset
1436
1521
// and larger trailing slice element works.
1437
1522
let mut bytes = [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] ;
@@ -1440,6 +1525,16 @@ mod tests {
1440
1525
let slice_dst_small = SliceDst :: < U16 , u8 > :: mut_from_bytes ( & mut bytes[ ..] ) . unwrap ( ) ;
1441
1526
let x: & mut SliceDst < U16 , u8 > = transmute_mut ! ( slice_dst_big) ;
1442
1527
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) ;
1443
1538
}
1444
1539
1445
1540
#[ test]
0 commit comments