crc32c/util.rs
1use std::ptr::NonNull;
2use std::{cmp, slice};
3
4/// A newtype wrapper for a little endian `u64`.
5///
6/// It is safe to transmute between a `u64` and `U64Le`.
7#[repr(transparent)]
8#[derive(Clone, Copy)]
9pub(crate) struct U64Le(u64);
10
11impl U64Le {
12 /// Returns a `u64` with correct endianness for the target.
13 ///
14 /// On little endian targets, this is a no-op.
15 #[allow(clippy::inline_always)]
16 #[inline(always)]
17 pub const fn get(self) -> u64 {
18 u64::from_le(self.0)
19 }
20}
21
22/// Splits a buffer into three subslices:
23/// - the first one is up to the first 8-byte aligned address.
24/// - the second one is 8-byte aligned and its length is a multiple of 8.
25/// - the third one is 8-byte aligned but its length is less than 8.
26pub(crate) fn split(buffer: &[u8]) -> (&[u8], &[U64Le], &[u8]) {
27 let (start, mid) = {
28 let split_index = {
29 let addr = buffer.as_ptr() as usize;
30
31 // Align to multiples of 8.
32 let aligned_addr = (addr + 7) & (!7);
33
34 // Index of the next aligned element.
35 let next_i = aligned_addr - addr;
36
37 // Buffer might be too small.
38 cmp::min(next_i, buffer.len())
39 };
40
41 buffer.split_at(split_index)
42 };
43
44 let (mid, end) = {
45 // Round length down to multiples of 8.
46 let split_index = mid.len() & (!7);
47
48 mid.split_at(split_index)
49 };
50
51 let mid = unsafe {
52 let length = mid.len() / 8;
53 let ptr = if length == 0 {
54 // `slice::from_raw_parts` requires that pointers be nonnull and
55 // aligned even for zero-length slices.
56 NonNull::<U64Le>::dangling().as_ptr()
57 } else {
58 #[allow(clippy::cast_ptr_alignment)]
59 mid.as_ptr().cast::<U64Le>()
60 };
61
62 slice::from_raw_parts(ptr, length)
63 };
64
65 (start, mid, end)
66}