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}