@@ -138,6 +138,24 @@ macro_rules! transmute {
138
138
/// assert_eq!(size_of_val(src), size_of_val(dst));
139
139
/// ```
140
140
///
141
+ /// ## `#![allow(shrink)]`
142
+ ///
143
+ /// If `#![allow(shrink)]` is provided, `transmute_ref!` additionally supports
144
+ /// transmutations that shrink the size of the referent; e.g.:
145
+ ///
146
+ /// ```
147
+ /// # use zerocopy::transmute_ref;
148
+ /// # use core::mem::size_of_val; // Not in the prelude on our MSRV
149
+ /// let src: &[[u8; 3]] = &[[0, 1, 2], [3, 4, 5], [6, 7, 8]][..];
150
+ /// let dst: &[[u8; 2]] = transmute_ref!(#![allow(shrink)] src);
151
+ ///
152
+ /// assert_eq!(src.len(), 3);
153
+ /// assert_eq!(dst.len(), 4);
154
+ /// assert_eq!(size_of_val(src), 9);
155
+ /// assert_eq!(size_of_val(dst), 8);
156
+ /// assert_eq!(dst, [[0, 1], [2, 3], [4, 5], [6, 7]]);
157
+ /// ```
158
+ ///
141
159
/// # Errors
142
160
///
143
161
/// Violations of the alignment and size compatibility checks are detected
@@ -224,7 +242,18 @@ macro_rules! transmute {
224
242
/// `Dst: Sized`.
225
243
#[ macro_export]
226
244
macro_rules! transmute_ref {
227
- ( $e: expr) => { {
245
+ ( #![ allow( shrink) ] $e: expr) => {
246
+ $crate:: __transmute_ref_inner!( true , $e)
247
+ } ;
248
+ ( $e: expr) => {
249
+ $crate:: __transmute_ref_inner!( false , $e)
250
+ } ;
251
+ }
252
+
253
+ #[ macro_export]
254
+ #[ doc( hidden) ]
255
+ macro_rules! __transmute_ref_inner {
256
+ ( $allow_shrink: literal, $e: expr) => { {
228
257
// NOTE: This must be a macro (rather than a function with trait bounds)
229
258
// because there's no way, in a generic context, to enforce that two
230
259
// types have the same size or alignment.
@@ -263,10 +292,10 @@ macro_rules! transmute_ref {
263
292
// - `Src: IntoBytes + Immutable`
264
293
// - `Dst: FromBytes + Immutable`
265
294
unsafe {
266
- t. transmute_ref( )
295
+ t. transmute_ref:: <$allow_shrink> ( )
267
296
}
268
297
}
269
- } }
298
+ } } ;
270
299
}
271
300
272
301
/// Safely transmutes a mutable reference of one type to a mutable reference of
@@ -312,6 +341,29 @@ macro_rules! transmute_ref {
312
341
/// assert_eq!(size_of_val(src), dst_size);
313
342
/// ```
314
343
///
344
+ /// ## `#![allow(shrink)]`
345
+ ///
346
+ /// If `#![allow(shrink)]` is provided, `transmute_mut!` additionally supports
347
+ /// transmutations that shrink the size of the referent; e.g.:
348
+ ///
349
+ /// ```
350
+ /// # use zerocopy::transmute_mut;
351
+ /// # use core::mem::size_of_val; // Not in the prelude on our MSRV
352
+ /// let src: &mut [[u8; 3]] = &mut [[0, 1, 2], [3, 4, 5], [6, 7, 8]][..];
353
+ /// let dst: &mut [[u8; 2]] = transmute_mut!(#![allow(shrink)] src);
354
+ ///
355
+ ///
356
+ /// let dst_len = dst.len();
357
+ /// let dst_size = size_of_val(dst);
358
+ /// assert_eq!(dst, [[0, 1], [2, 3], [4, 5], [6, 7]]);
359
+ ///
360
+ /// assert_eq!(src.len(), 3);
361
+ /// assert_eq!(dst_len, 4);
362
+ ///
363
+ /// assert_eq!(size_of_val(src), 9);
364
+ /// assert_eq!(dst_size, 8);
365
+ /// ```
366
+ ///
315
367
/// # Errors
316
368
///
317
369
/// Violations of the alignment and size compatibility checks are detected
@@ -400,7 +452,18 @@ macro_rules! transmute_ref {
400
452
/// ```
401
453
#[ macro_export]
402
454
macro_rules! transmute_mut {
403
- ( $e: expr) => { {
455
+ ( #![ allow( shrink) ] $e: expr) => {
456
+ $crate:: __transmute_mut_inner!( true , $e)
457
+ } ;
458
+ ( $e: expr) => {
459
+ $crate:: __transmute_mut_inner!( false , $e)
460
+ } ;
461
+ }
462
+
463
+ #[ doc( hidden) ]
464
+ #[ macro_export]
465
+ macro_rules! __transmute_mut_inner {
466
+ ( $allow_shrink: literal, $e: expr) => { {
404
467
// NOTE: This must be a macro (rather than a function with trait bounds)
405
468
// because, for backwards-compatibility on v0.8.x, we use the autoref
406
469
// specialization trick to dispatch to different `transmute_mut`
@@ -414,7 +477,7 @@ macro_rules! transmute_mut {
414
477
#[ allow( unused) ]
415
478
use $crate:: util:: macro_util:: TransmuteMutDst as _;
416
479
let t = $crate:: util:: macro_util:: Wrap :: new( e) ;
417
- t. transmute_mut( )
480
+ t. transmute_mut:: <$allow_shrink> ( )
418
481
} }
419
482
}
420
483
@@ -1154,6 +1217,11 @@ mod tests {
1154
1217
let slice_of_u16s: & [ U16 ] = <[ U16 ] >:: ref_from_bytes ( & [ 0 , 1 , 2 , 3 , 4 , 5 ] [ ..] ) . unwrap ( ) ;
1155
1218
assert_eq ! ( x, slice_of_u16s) ;
1156
1219
1220
+ // Test that transmuting from a larger sized type to a smaller sized
1221
+ // type works.
1222
+ let x: & u8 = transmute_ref ! ( #![ allow( shrink) ] & 0u16 ) ;
1223
+ assert_eq ! ( * x, 0 ) ;
1224
+
1157
1225
// Test that transmuting from a type with larger trailing slice offset
1158
1226
// and larger trailing slice element works.
1159
1227
let bytes = & [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] [ ..] ;
@@ -1162,6 +1230,15 @@ mod tests {
1162
1230
let x: & SliceDst < U16 , u8 > = transmute_ref ! ( slice_dst_big) ;
1163
1231
assert_eq ! ( x, slice_dst_small) ;
1164
1232
1233
+ let bytes = & [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] [ ..] ;
1234
+ let slice_dst_big = SliceDst :: < [ u8 ; 4 ] , [ u8 ; 4 ] > :: ref_from_bytes ( bytes) . unwrap ( ) ;
1235
+ let slice_dst_small = SliceDst :: < [ u8 ; 3 ] , [ u8 ; 3 ] > :: ref_from_bytes ( & bytes[ ..6 ] ) . unwrap ( ) ;
1236
+ let x: & SliceDst < [ u8 ; 3 ] , [ u8 ; 3 ] > = transmute_ref ! (
1237
+ #![ allow( shrink) ]
1238
+ slice_dst_big
1239
+ ) ;
1240
+ assert_eq ! ( x, slice_dst_small) ;
1241
+
1165
1242
// Test that it's legal to transmute a reference while shrinking the
1166
1243
// lifetime (note that `X` has the lifetime `'static`).
1167
1244
let x: & [ u8 ; 8 ] = transmute_ref ! ( X ) ;
@@ -1342,6 +1419,14 @@ mod tests {
1342
1419
let x: & mut [ i16 ] = transmute_mut ! ( array_of_u16s) ;
1343
1420
assert_eq ! ( x, array_of_i16s) ;
1344
1421
1422
+ // Test that transmuting from a larger sized type to a smaller sized
1423
+ // type works.
1424
+ let mut large: [ u8 ; 2 ] = [ 1 , 1 ] ;
1425
+ let x: & mut u8 = transmute_mut ! ( #![ allow( shrink) ] & mut large) ;
1426
+ assert_eq ! ( * x, 1 ) ;
1427
+ * x = 0 ;
1428
+ assert_eq ! ( large, [ 0 , 1 ] ) ;
1429
+
1345
1430
// Test that transmuting from a type with larger trailing slice offset
1346
1431
// and larger trailing slice element works.
1347
1432
let mut bytes = [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] ;
@@ -1350,6 +1435,16 @@ mod tests {
1350
1435
let slice_dst_small = SliceDst :: < U16 , u8 > :: mut_from_bytes ( & mut bytes[ ..] ) . unwrap ( ) ;
1351
1436
let x: & mut SliceDst < U16 , u8 > = transmute_mut ! ( slice_dst_big) ;
1352
1437
assert_eq ! ( x, slice_dst_small) ;
1438
+
1439
+ let mut bytes = [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] ;
1440
+ let slice_dst_big = SliceDst :: < [ u8 ; 4 ] , [ u8 ; 4 ] > :: mut_from_bytes ( & mut bytes[ ..] ) . unwrap ( ) ;
1441
+ let mut bytes = [ 0 , 1 , 2 , 3 , 4 , 5 ] ;
1442
+ let slice_dst_small = SliceDst :: < [ u8 ; 3 ] , [ u8 ; 3 ] > :: mut_from_bytes ( & mut bytes[ ..] ) . unwrap ( ) ;
1443
+ let x: & mut SliceDst < [ u8 ; 3 ] , [ u8 ; 3 ] > = transmute_mut ! (
1444
+ #![ allow( shrink) ]
1445
+ slice_dst_big
1446
+ ) ;
1447
+ assert_eq ! ( x, slice_dst_small) ;
1353
1448
}
1354
1449
1355
1450
#[ test]
0 commit comments