1 #![allow(clippy::unnecessary_cast)]
2 #![allow(clippy::manual_slice_size_calculation)]
3 
4 use core::{
5   mem::size_of,
6   num::{NonZeroU32, NonZeroU8},
7 };
8 
9 use bytemuck::{checked::CheckedCastError, *};
10 
11 #[test]
test_try_cast_slice()12 fn test_try_cast_slice() {
13   // some align4 data
14   let nonzero_u32_slice: &[NonZeroU32] = &[
15     NonZeroU32::new(4).unwrap(),
16     NonZeroU32::new(5).unwrap(),
17     NonZeroU32::new(6).unwrap(),
18   ];
19 
20   // contains bytes with invalid bitpattern for NonZeroU8
21   assert_eq!(
22     checked::try_cast_slice::<NonZeroU32, NonZeroU8>(nonzero_u32_slice),
23     Err(CheckedCastError::InvalidBitPattern)
24   );
25 
26   // the same data as align1
27   let the_bytes: &[u8] = checked::try_cast_slice(nonzero_u32_slice).unwrap();
28 
29   assert_eq!(
30     nonzero_u32_slice.as_ptr() as *const NonZeroU32 as usize,
31     the_bytes.as_ptr() as *const u8 as usize
32   );
33   assert_eq!(
34     nonzero_u32_slice.len() * size_of::<NonZeroU32>(),
35     the_bytes.len() * size_of::<u8>()
36   );
37 
38   // by taking one byte off the front, we're definitely mis-aligned for
39   // NonZeroU32.
40   let mis_aligned_bytes = &the_bytes[1..];
41   assert_eq!(
42     checked::try_cast_slice::<u8, NonZeroU32>(mis_aligned_bytes),
43     Err(CheckedCastError::PodCastError(
44       PodCastError::TargetAlignmentGreaterAndInputNotAligned
45     ))
46   );
47 
48   // by taking one byte off the end, we're aligned but would have slop bytes for
49   // NonZeroU32
50   let the_bytes_len_minus1 = the_bytes.len() - 1;
51   let slop_bytes = &the_bytes[..the_bytes_len_minus1];
52   assert_eq!(
53     checked::try_cast_slice::<u8, NonZeroU32>(slop_bytes),
54     Err(CheckedCastError::PodCastError(PodCastError::OutputSliceWouldHaveSlop))
55   );
56 
57   // if we don't mess with it we can up-alignment cast
58   checked::try_cast_slice::<u8, NonZeroU32>(the_bytes).unwrap();
59 }
60 
61 #[test]
test_try_cast_slice_mut()62 fn test_try_cast_slice_mut() {
63   // some align4 data
64   let u32_slice: &mut [u32] = &mut [4, 5, 6];
65 
66   // contains bytes with invalid bitpattern for NonZeroU8
67   assert_eq!(
68     checked::try_cast_slice_mut::<u32, NonZeroU8>(u32_slice),
69     Err(CheckedCastError::InvalidBitPattern)
70   );
71 
72   // some align4 data
73   let u32_slice: &mut [u32] = &mut [0x4444_4444, 0x5555_5555, 0x6666_6666];
74   let u32_len = u32_slice.len();
75   let u32_ptr = u32_slice.as_ptr();
76 
77   // the same data as align1, nonzero bytes
78   let the_nonzero_bytes: &mut [NonZeroU8] =
79     checked::try_cast_slice_mut(u32_slice).unwrap();
80   let the_nonzero_bytes_len = the_nonzero_bytes.len();
81   let the_nonzero_bytes_ptr = the_nonzero_bytes.as_ptr();
82 
83   assert_eq!(
84     u32_ptr as *const u32 as usize,
85     the_nonzero_bytes_ptr as *const NonZeroU8 as usize
86   );
87   assert_eq!(
88     u32_len * size_of::<u32>(),
89     the_nonzero_bytes_len * size_of::<NonZeroU8>()
90   );
91 
92   // the same data as align1
93   let the_bytes: &mut [u8] = checked::try_cast_slice_mut(u32_slice).unwrap();
94   let the_bytes_len = the_bytes.len();
95   let the_bytes_ptr = the_bytes.as_ptr();
96 
97   assert_eq!(
98     u32_ptr as *const u32 as usize,
99     the_bytes_ptr as *const u8 as usize
100   );
101   assert_eq!(
102     u32_len * size_of::<u32>(),
103     the_bytes_len * size_of::<NonZeroU8>()
104   );
105 
106   // by taking one byte off the front, we're definitely mis-aligned for u32.
107   let mis_aligned_bytes = &mut the_bytes[1..];
108   assert_eq!(
109     checked::try_cast_slice_mut::<u8, NonZeroU32>(mis_aligned_bytes),
110     Err(CheckedCastError::PodCastError(
111       PodCastError::TargetAlignmentGreaterAndInputNotAligned
112     ))
113   );
114 
115   // by taking one byte off the end, we're aligned but would have slop bytes for
116   // NonZeroU32
117   let the_bytes_len_minus1 = the_bytes.len() - 1;
118   let slop_bytes = &mut the_bytes[..the_bytes_len_minus1];
119   assert_eq!(
120     checked::try_cast_slice_mut::<u8, NonZeroU32>(slop_bytes),
121     Err(CheckedCastError::PodCastError(PodCastError::OutputSliceWouldHaveSlop))
122   );
123 
124   // if we don't mess with it we can up-alignment cast, since there are no
125   // zeroes in the original slice
126   checked::try_cast_slice_mut::<u8, NonZeroU32>(the_bytes).unwrap();
127 }
128 
129 #[test]
test_types()130 fn test_types() {
131   let _: NonZeroU32 = checked::cast(1.0_f32);
132   let _: &mut NonZeroU32 = checked::cast_mut(&mut 1.0_f32);
133   let _: &NonZeroU32 = checked::cast_ref(&1.0_f32);
134   let _: &[NonZeroU32] = checked::cast_slice(&[1.0_f32]);
135   let _: &mut [NonZeroU32] = checked::cast_slice_mut(&mut [1.0_f32]);
136   //
137   let _: Result<NonZeroU32, CheckedCastError> = checked::try_cast(1.0_f32);
138   let _: Result<&mut NonZeroU32, CheckedCastError> =
139     checked::try_cast_mut(&mut 1.0_f32);
140   let _: Result<&NonZeroU32, CheckedCastError> =
141     checked::try_cast_ref(&1.0_f32);
142   let _: Result<&[NonZeroU32], CheckedCastError> =
143     checked::try_cast_slice(&[1.0_f32]);
144   let _: Result<&mut [NonZeroU32], CheckedCastError> =
145     checked::try_cast_slice_mut(&mut [1.0_f32]);
146 }
147 
148 #[test]
test_try_pod_read_unaligned()149 fn test_try_pod_read_unaligned() {
150   let u32s = [0xaabbccdd, 0x11223344_u32];
151   let bytes = bytemuck::checked::cast_slice::<u32, u8>(&u32s);
152 
153   #[cfg(target_endian = "big")]
154   assert_eq!(
155     checked::try_pod_read_unaligned::<NonZeroU32>(&bytes[1..5]),
156     Ok(NonZeroU32::new(0xbbccdd11).unwrap())
157   );
158   #[cfg(target_endian = "little")]
159   assert_eq!(
160     checked::try_pod_read_unaligned::<NonZeroU32>(&bytes[1..5]),
161     Ok(NonZeroU32::new(0x44aabbcc).unwrap())
162   );
163 
164   let u32s = [0; 2];
165   let bytes = bytemuck::checked::cast_slice::<u32, u8>(&u32s);
166 
167   assert_eq!(
168     checked::try_pod_read_unaligned::<NonZeroU32>(&bytes[1..5]),
169     Err(CheckedCastError::InvalidBitPattern)
170   );
171 }
172 
173 #[test]
test_try_from_bytes()174 fn test_try_from_bytes() {
175   let nonzero_u32s = [
176     NonZeroU32::new(0xaabbccdd).unwrap(),
177     NonZeroU32::new(0x11223344).unwrap(),
178   ];
179   let bytes = bytemuck::checked::cast_slice::<NonZeroU32, u8>(&nonzero_u32s);
180   assert_eq!(
181     checked::try_from_bytes::<NonZeroU32>(&bytes[..4]),
182     Ok(&nonzero_u32s[0])
183   );
184   assert_eq!(
185     checked::try_from_bytes::<NonZeroU32>(&bytes[..5]),
186     Err(CheckedCastError::PodCastError(PodCastError::SizeMismatch))
187   );
188   assert_eq!(
189     checked::try_from_bytes::<NonZeroU32>(&bytes[..3]),
190     Err(CheckedCastError::PodCastError(PodCastError::SizeMismatch))
191   );
192   assert_eq!(
193     checked::try_from_bytes::<NonZeroU32>(&bytes[1..5]),
194     Err(CheckedCastError::PodCastError(
195       PodCastError::TargetAlignmentGreaterAndInputNotAligned
196     ))
197   );
198 
199   let zero_u32s = [0, 0x11223344_u32];
200   let bytes = bytemuck::checked::cast_slice::<u32, u8>(&zero_u32s);
201   assert_eq!(
202     checked::try_from_bytes::<NonZeroU32>(&bytes[..4]),
203     Err(CheckedCastError::InvalidBitPattern)
204   );
205   assert_eq!(
206     checked::try_from_bytes::<NonZeroU32>(&bytes[4..]),
207     Ok(&NonZeroU32::new(zero_u32s[1]).unwrap())
208   );
209   assert_eq!(
210     checked::try_from_bytes::<NonZeroU32>(&bytes[..5]),
211     Err(CheckedCastError::PodCastError(PodCastError::SizeMismatch))
212   );
213   assert_eq!(
214     checked::try_from_bytes::<NonZeroU32>(&bytes[..3]),
215     Err(CheckedCastError::PodCastError(PodCastError::SizeMismatch))
216   );
217   assert_eq!(
218     checked::try_from_bytes::<NonZeroU32>(&bytes[1..5]),
219     Err(CheckedCastError::PodCastError(
220       PodCastError::TargetAlignmentGreaterAndInputNotAligned
221     ))
222   );
223 }
224 
225 #[test]
test_try_from_bytes_mut()226 fn test_try_from_bytes_mut() {
227   let a = 0xaabbccdd_u32;
228   let b = 0x11223344_u32;
229   let mut u32s = [a, b];
230   let bytes = bytemuck::checked::cast_slice_mut::<u32, u8>(&mut u32s);
231   assert_eq!(
232     checked::try_from_bytes_mut::<NonZeroU32>(&mut bytes[..4]),
233     Ok(&mut NonZeroU32::new(a).unwrap())
234   );
235   assert_eq!(
236     checked::try_from_bytes_mut::<NonZeroU32>(&mut bytes[4..]),
237     Ok(&mut NonZeroU32::new(b).unwrap())
238   );
239   assert_eq!(
240     checked::try_from_bytes_mut::<NonZeroU32>(&mut bytes[..5]),
241     Err(CheckedCastError::PodCastError(PodCastError::SizeMismatch))
242   );
243   assert_eq!(
244     checked::try_from_bytes_mut::<NonZeroU32>(&mut bytes[..3]),
245     Err(CheckedCastError::PodCastError(PodCastError::SizeMismatch))
246   );
247   assert_eq!(
248     checked::try_from_bytes::<NonZeroU32>(&bytes[1..5]),
249     Err(CheckedCastError::PodCastError(
250       PodCastError::TargetAlignmentGreaterAndInputNotAligned
251     ))
252   );
253 
254   let mut u32s = [0, b];
255   let bytes = bytemuck::checked::cast_slice_mut::<u32, u8>(&mut u32s);
256   assert_eq!(
257     checked::try_from_bytes_mut::<NonZeroU32>(&mut bytes[..4]),
258     Err(CheckedCastError::InvalidBitPattern)
259   );
260   assert_eq!(
261     checked::try_from_bytes_mut::<NonZeroU32>(&mut bytes[4..]),
262     Ok(&mut NonZeroU32::new(b).unwrap())
263   );
264   assert_eq!(
265     checked::try_from_bytes_mut::<NonZeroU32>(&mut bytes[..5]),
266     Err(CheckedCastError::PodCastError(PodCastError::SizeMismatch))
267   );
268   assert_eq!(
269     checked::try_from_bytes_mut::<NonZeroU32>(&mut bytes[..3]),
270     Err(CheckedCastError::PodCastError(PodCastError::SizeMismatch))
271   );
272   assert_eq!(
273     checked::try_from_bytes::<NonZeroU32>(&bytes[1..5]),
274     Err(CheckedCastError::PodCastError(
275       PodCastError::TargetAlignmentGreaterAndInputNotAligned
276     ))
277   );
278 }
279 
280 #[test]
test_from_bytes()281 fn test_from_bytes() {
282   let abcd = 0xaabbccdd_u32;
283   let aligned_bytes = bytemuck::bytes_of(&abcd);
284   assert_eq!(
285     checked::from_bytes::<NonZeroU32>(aligned_bytes),
286     &NonZeroU32::new(abcd).unwrap()
287   );
288   assert!(core::ptr::eq(
289     checked::from_bytes(aligned_bytes) as *const NonZeroU32 as *const u32,
290     &abcd
291   ));
292 }
293 
294 #[test]
test_from_bytes_mut()295 fn test_from_bytes_mut() {
296   let mut a = 0xaabbccdd_u32;
297   let a_addr = &a as *const _ as usize;
298   let aligned_bytes = bytemuck::bytes_of_mut(&mut a);
299   assert_eq!(
300     *checked::from_bytes_mut::<NonZeroU32>(aligned_bytes),
301     NonZeroU32::new(0xaabbccdd).unwrap()
302   );
303   assert_eq!(
304     checked::from_bytes_mut::<NonZeroU32>(aligned_bytes) as *const NonZeroU32
305       as usize,
306     a_addr
307   );
308 }
309 
310 // like #[should_panic], but can be a part of another test, instead of requiring
311 // it to be it's own test.
312 macro_rules! should_panic {
313   ($ex:expr) => {
314     assert!(
315       std::panic::catch_unwind(|| {
316         let _ = $ex;
317       })
318       .is_err(),
319       concat!("should have panicked: `", stringify!($ex), "`")
320     );
321   };
322 }
323 
324 #[test]
test_panics()325 fn test_panics() {
326   should_panic!(checked::cast::<u32, NonZeroU32>(0));
327   should_panic!(checked::cast_ref::<u32, NonZeroU32>(&0));
328   should_panic!(checked::cast_mut::<u32, NonZeroU32>(&mut 0));
329   should_panic!(checked::cast_slice::<u8, NonZeroU32>(&[1u8, 2u8]));
330   should_panic!(checked::cast_slice_mut::<u8, NonZeroU32>(&mut [1u8, 2u8]));
331   should_panic!(checked::from_bytes::<NonZeroU32>(&[1u8, 2]));
332   should_panic!(checked::from_bytes::<NonZeroU32>(&[1u8, 2, 3, 4, 5]));
333   should_panic!(checked::from_bytes_mut::<NonZeroU32>(&mut [1u8, 2]));
334   should_panic!(checked::from_bytes_mut::<NonZeroU32>(&mut [1u8, 2, 3, 4, 5]));
335   // use cast_slice on some u32s to get some align>=4 bytes, so we can know
336   // we'll give from_bytes unaligned ones.
337   let aligned_bytes = bytemuck::cast_slice::<u32, u8>(&[0, 0]);
338   should_panic!(checked::from_bytes::<NonZeroU32>(aligned_bytes));
339   should_panic!(checked::from_bytes::<NonZeroU32>(&aligned_bytes[1..5]));
340   should_panic!(checked::pod_read_unaligned::<NonZeroU32>(
341     &aligned_bytes[1..5]
342   ));
343 }
344 
345 #[test]
test_char()346 fn test_char() {
347   assert_eq!(checked::try_cast::<u32, char>(0), Ok('\0'));
348   assert_eq!(checked::try_cast::<u32, char>(0xd7ff), Ok('\u{d7ff}'));
349   assert_eq!(
350     checked::try_cast::<u32, char>(0xd800),
351     Err(CheckedCastError::InvalidBitPattern)
352   );
353   assert_eq!(
354     checked::try_cast::<u32, char>(0xdfff),
355     Err(CheckedCastError::InvalidBitPattern)
356   );
357   assert_eq!(checked::try_cast::<u32, char>(0xe000), Ok('\u{e000}'));
358   assert_eq!(checked::try_cast::<u32, char>(0x10ffff), Ok('\u{10ffff}'));
359   assert_eq!(
360     checked::try_cast::<u32, char>(0x110000),
361     Err(CheckedCastError::InvalidBitPattern)
362   );
363   assert_eq!(
364     checked::try_cast::<u32, char>(-1i32 as u32),
365     Err(CheckedCastError::InvalidBitPattern)
366   );
367 }
368 
369 #[test]
test_bool()370 fn test_bool() {
371   assert_eq!(checked::try_cast::<u8, bool>(0), Ok(false));
372   assert_eq!(checked::try_cast::<u8, bool>(1), Ok(true));
373   for i in 2..=255 {
374     assert_eq!(
375       checked::try_cast::<u8, bool>(i),
376       Err(CheckedCastError::InvalidBitPattern)
377     );
378   }
379 
380   assert_eq!(checked::try_from_bytes::<bool>(&[1]), Ok(&true));
381   assert_eq!(
382     checked::try_from_bytes::<bool>(&[3]),
383     Err(CheckedCastError::InvalidBitPattern)
384   );
385   assert_eq!(
386     checked::try_from_bytes::<bool>(&[0, 1]),
387     Err(CheckedCastError::PodCastError(PodCastError::SizeMismatch))
388   );
389 }
390 
391 #[test]
test_all_nonzero()392 fn test_all_nonzero() {
393   use core::num::*;
394   macro_rules! test_nonzero {
395     ($nonzero:ty: $primitive:ty) => {
396       assert_eq!(
397         checked::try_cast::<$primitive, $nonzero>(0),
398         Err(CheckedCastError::InvalidBitPattern)
399       );
400       assert_eq!(
401         checked::try_cast::<$primitive, $nonzero>(1),
402         Ok(<$nonzero>::new(1).unwrap())
403       );
404     };
405   }
406 
407   test_nonzero!(NonZeroU8: u8);
408   test_nonzero!(NonZeroI8: i8);
409   test_nonzero!(NonZeroU16: u16);
410   test_nonzero!(NonZeroI16: i16);
411   test_nonzero!(NonZeroU32: u32);
412   test_nonzero!(NonZeroI32: i32);
413   test_nonzero!(NonZeroU64: u64);
414   test_nonzero!(NonZeroI64: i64);
415   test_nonzero!(NonZeroU128: u128);
416   test_nonzero!(NonZeroI128: i128);
417   test_nonzero!(NonZeroUsize: usize);
418   test_nonzero!(NonZeroIsize: isize);
419 }
420