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