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