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