@@ -185,6 +185,24 @@ macro_rules! transmute {
185
185
/// assert_eq!(size_of_val(src), size_of_val(dst));
186
186
/// ```
187
187
///
188
+ /// ## `#![allow(shrink)]`
189
+ ///
190
+ /// If `#![allow(shrink)]` is provided, `transmute_ref!` additionally supports
191
+ /// transmutations that shrink the size of the referent; e.g.:
192
+ ///
193
+ /// ```
194
+ /// # use zerocopy::transmute_ref;
195
+ /// # use core::mem::size_of_val; // Not in the prelude on our MSRV
196
+ /// let src: &[[u8; 3]] = &[[0, 1, 2], [3, 4, 5], [6, 7, 8]][..];
197
+ /// let dst: &[[u8; 2]] = transmute_ref!(#![allow(shrink)] src);
198
+ ///
199
+ /// assert_eq!(src.len(), 3);
200
+ /// assert_eq!(dst.len(), 4);
201
+ /// assert_eq!(size_of_val(src), 9);
202
+ /// assert_eq!(size_of_val(dst), 8);
203
+ /// assert_eq!(dst, [[0, 1], [2, 3], [4, 5], [6, 7]]);
204
+ /// ```
205
+ ///
188
206
/// # Errors
189
207
///
190
208
/// Violations of the alignment and size compatibility checks are detected
@@ -271,7 +289,18 @@ macro_rules! transmute {
271
289
/// `Dst: Sized`.
272
290
#[ macro_export]
273
291
macro_rules! transmute_ref {
274
- ( $e: expr) => { {
292
+ ( #![ allow( shrink) ] $e: expr) => {
293
+ $crate:: __transmute_ref_inner!( true , $e)
294
+ } ;
295
+ ( $e: expr) => {
296
+ $crate:: __transmute_ref_inner!( false , $e)
297
+ } ;
298
+ }
299
+
300
+ #[ macro_export]
301
+ #[ doc( hidden) ]
302
+ macro_rules! __transmute_ref_inner {
303
+ ( $allow_shrink: literal, $e: expr) => { {
275
304
// NOTE: This must be a macro (rather than a function with trait bounds)
276
305
// because there's no way, in a generic context, to enforce that two
277
306
// types have the same size or alignment.
@@ -310,10 +339,10 @@ macro_rules! transmute_ref {
310
339
// - `Src: IntoBytes + Immutable`
311
340
// - `Dst: FromBytes + Immutable`
312
341
unsafe {
313
- t. transmute_ref( )
342
+ t. transmute_ref:: <$allow_shrink> ( )
314
343
}
315
344
}
316
- } }
345
+ } } ;
317
346
}
318
347
319
348
/// Safely transmutes a mutable reference of one type to a mutable reference of
@@ -359,6 +388,29 @@ macro_rules! transmute_ref {
359
388
/// assert_eq!(size_of_val(src), dst_size);
360
389
/// ```
361
390
///
391
+ /// ## `#![allow(shrink)]`
392
+ ///
393
+ /// If `#![allow(shrink)]` is provided, `transmute_mut!` additionally supports
394
+ /// transmutations that shrink the size of the referent; e.g.:
395
+ ///
396
+ /// ```
397
+ /// # use zerocopy::transmute_mut;
398
+ /// # use core::mem::size_of_val; // Not in the prelude on our MSRV
399
+ /// let src: &mut [[u8; 3]] = &mut [[0, 1, 2], [3, 4, 5], [6, 7, 8]][..];
400
+ /// let dst: &mut [[u8; 2]] = transmute_mut!(#![allow(shrink)] src);
401
+ ///
402
+ ///
403
+ /// let dst_len = dst.len();
404
+ /// let dst_size = size_of_val(dst);
405
+ /// assert_eq!(dst, [[0, 1], [2, 3], [4, 5], [6, 7]]);
406
+ ///
407
+ /// assert_eq!(src.len(), 3);
408
+ /// assert_eq!(dst_len, 4);
409
+ ///
410
+ /// assert_eq!(size_of_val(src), 9);
411
+ /// assert_eq!(dst_size, 8);
412
+ /// ```
413
+ ///
362
414
/// # Errors
363
415
///
364
416
/// Violations of the alignment and size compatibility checks are detected
@@ -447,7 +499,18 @@ macro_rules! transmute_ref {
447
499
/// ```
448
500
#[ macro_export]
449
501
macro_rules! transmute_mut {
450
- ( $e: expr) => { {
502
+ ( #![ allow( shrink) ] $e: expr) => {
503
+ $crate:: __transmute_mut_inner!( true , $e)
504
+ } ;
505
+ ( $e: expr) => {
506
+ $crate:: __transmute_mut_inner!( false , $e)
507
+ } ;
508
+ }
509
+
510
+ #[ doc( hidden) ]
511
+ #[ macro_export]
512
+ macro_rules! __transmute_mut_inner {
513
+ ( $allow_shrink: literal, $e: expr) => { {
451
514
// NOTE: This must be a macro (rather than a function with trait bounds)
452
515
// because, for backwards-compatibility on v0.8.x, we use the autoref
453
516
// specialization trick to dispatch to different `transmute_mut`
@@ -461,7 +524,7 @@ macro_rules! transmute_mut {
461
524
#[ allow( unused) ]
462
525
use $crate:: util:: macro_util:: TransmuteMutDst as _;
463
526
let t = $crate:: util:: macro_util:: Wrap :: new( e) ;
464
- t. transmute_mut( )
527
+ t. transmute_mut:: <$allow_shrink> ( )
465
528
} }
466
529
}
467
530
@@ -1209,6 +1272,11 @@ mod tests {
1209
1272
let slice_of_u16s: & [ U16 ] = <[ U16 ] >:: ref_from_bytes ( & [ 0 , 1 , 2 , 3 , 4 , 5 ] [ ..] ) . unwrap ( ) ;
1210
1273
assert_eq ! ( x, slice_of_u16s) ;
1211
1274
1275
+ // Test that transmuting from a larger sized type to a smaller sized
1276
+ // type works.
1277
+ let x: & u8 = transmute_ref ! ( #![ allow( shrink) ] & 0u16 ) ;
1278
+ assert_eq ! ( * x, 0 ) ;
1279
+
1212
1280
// Test that transmuting from a type with larger trailing slice offset
1213
1281
// and larger trailing slice element works.
1214
1282
let bytes = & [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] [ ..] ;
@@ -1217,6 +1285,15 @@ mod tests {
1217
1285
let x: & SliceDst < U16 , u8 > = transmute_ref ! ( slice_dst_big) ;
1218
1286
assert_eq ! ( x, slice_dst_small) ;
1219
1287
1288
+ let bytes = & [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] [ ..] ;
1289
+ let slice_dst_big = SliceDst :: < [ u8 ; 4 ] , [ u8 ; 4 ] > :: ref_from_bytes ( bytes) . unwrap ( ) ;
1290
+ let slice_dst_small = SliceDst :: < [ u8 ; 3 ] , [ u8 ; 3 ] > :: ref_from_bytes ( & bytes[ ..6 ] ) . unwrap ( ) ;
1291
+ let x: & SliceDst < [ u8 ; 3 ] , [ u8 ; 3 ] > = transmute_ref ! (
1292
+ #![ allow( shrink) ]
1293
+ slice_dst_big
1294
+ ) ;
1295
+ assert_eq ! ( x, slice_dst_small) ;
1296
+
1220
1297
// Test that it's legal to transmute a reference while shrinking the
1221
1298
// lifetime (note that `X` has the lifetime `'static`).
1222
1299
let x: & [ u8 ; 8 ] = transmute_ref ! ( X ) ;
@@ -1397,6 +1474,14 @@ mod tests {
1397
1474
let x: & mut [ i16 ] = transmute_mut ! ( array_of_u16s) ;
1398
1475
assert_eq ! ( x, array_of_i16s) ;
1399
1476
1477
+ // Test that transmuting from a larger sized type to a smaller sized
1478
+ // type works.
1479
+ let mut large: [ u8 ; 2 ] = [ 1 , 1 ] ;
1480
+ let x: & mut u8 = transmute_mut ! ( #![ allow( shrink) ] & mut large) ;
1481
+ assert_eq ! ( * x, 1 ) ;
1482
+ * x = 0 ;
1483
+ assert_eq ! ( large, [ 0 , 1 ] ) ;
1484
+
1400
1485
// Test that transmuting from a type with larger trailing slice offset
1401
1486
// and larger trailing slice element works.
1402
1487
let mut bytes = [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] ;
@@ -1405,6 +1490,16 @@ mod tests {
1405
1490
let slice_dst_small = SliceDst :: < U16 , u8 > :: mut_from_bytes ( & mut bytes[ ..] ) . unwrap ( ) ;
1406
1491
let x: & mut SliceDst < U16 , u8 > = transmute_mut ! ( slice_dst_big) ;
1407
1492
assert_eq ! ( x, slice_dst_small) ;
1493
+
1494
+ let mut bytes = [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] ;
1495
+ let slice_dst_big = SliceDst :: < [ u8 ; 4 ] , [ u8 ; 4 ] > :: mut_from_bytes ( & mut bytes[ ..] ) . unwrap ( ) ;
1496
+ let mut bytes = [ 0 , 1 , 2 , 3 , 4 , 5 ] ;
1497
+ let slice_dst_small = SliceDst :: < [ u8 ; 3 ] , [ u8 ; 3 ] > :: mut_from_bytes ( & mut bytes[ ..] ) . unwrap ( ) ;
1498
+ let x: & mut SliceDst < [ u8 ; 3 ] , [ u8 ; 3 ] > = transmute_mut ! (
1499
+ #![ allow( shrink) ]
1500
+ slice_dst_big
1501
+ ) ;
1502
+ assert_eq ! ( x, slice_dst_small) ;
1408
1503
}
1409
1504
1410
1505
#[ test]
0 commit comments