[go: up one dir, main page]

osi/
mem.rs

1//! # Basic functions for dealing with memory
2//!
3//! This module contains functions to help dealing with direct memory
4//! manipulation and inspection.
5
6use core::mem::transmute_copy;
7
8// Same as [`core::ptr::copy()`] but allows unaligned pointers.
9const unsafe fn copy_unaligned<T>(src: *const T, dst: *mut T, count: usize) {
10    // SAFETY: We can always alias raw-pointers temporarily. Rust has no
11    //         restriction on raw-pointer aliasing.
12    //         The size calculation is safe as the caller must guarantee
13    //         the source is within a single allocated object, and those
14    //         are limited in size to `isize::MAX`.
15    unsafe {
16        core::ptr::copy(
17            src as *const u8,
18            dst as *mut u8,
19            count * core::mem::size_of::<T>(),
20        )
21    }
22}
23
24/// Unsafely interprets a copy of `src` as `Dst`.
25///
26/// This function creates a byte-wise copy of the source data and unsafely
27/// interpets the result as a value of type `Dst`. The data is truncated or
28/// padded with uninitialized bytes, if necessary.
29///
30/// This is similar to [`core::mem::transmute_copy`] but allows the types to
31/// differ in size.
32///
33/// ## Safety
34///
35/// The caller must guarantee that a value of type `Dst` can be safely created
36/// with a byte-wise copy of `Src` (truncated or padded with uninitialized
37/// bytes, if their size does not match).
38#[inline]
39#[must_use]
40pub const unsafe fn transmute_copy_uninit<Src, Dst>(src: &Src) -> Dst {
41    if core::mem::size_of::<Src>() < core::mem::size_of::<Dst>() {
42        // The source is smaller in size than the destination. Hence, we need
43        // an uninitialized buffer that we copy into, and then yield to the
44        // caller. Trailing padding is left uninitialized.
45        //
46        // SAFETY: Delegated to the caller.
47        unsafe {
48            let mut dst = core::mem::MaybeUninit::<Dst>::uninit();
49            copy_unaligned(
50                src as *const Src,
51                dst.as_mut_ptr() as *mut Src,
52                1,
53            );
54            dst.assume_init()
55        }
56    } else {
57        // The source is larger in size than, or equal to, the destination.
58        // Hence, we can read out of the source value and ignore any trailing
59        // data. If the source is not suitably aligned, we must ensure a proper
60        // unaligned read instruction.
61        //
62        // SAFETY: Delegated to the caller.
63        unsafe {
64            if core::mem::align_of::<Dst>() > core::mem::align_of::<Src>() {
65                core::ptr::read_unaligned(src as *const Src as *const Dst)
66            } else {
67                core::ptr::read(src as *const Src as *const Dst)
68            }
69        }
70    }
71}
72
73// Manually create a copy of `v`, reversing the order of all underlying bytes.
74// This is the slow-path that manually iterates the individual bytes, but works
75// with any data-type, as long as the data-type stays valid with swapped bytes.
76const unsafe fn bswap_slow<T>(v: &T) -> T {
77    let mut r = core::mem::MaybeUninit::<T>::uninit();
78    let src = v as *const T as *const u8;
79    let dst = r.as_mut_ptr() as *mut u8;
80
81    unsafe {
82        let mut i = 0;
83        while i < core::mem::size_of::<T>() {
84            core::ptr::copy(
85                src.add(core::mem::size_of::<T>() - i - 1),
86                dst.add(i),
87                1,
88            );
89            i += 1;
90        }
91
92        r.assume_init()
93    }
94}
95
96/// Creates a copy of `v` with the order of all bytes reversed.
97///
98/// Creates a copy of `v` and reverses the order of all bytes underlying the
99/// object. That is, its last byte will be swapped with the first, its second
100/// to last byte will be swapped with the second, and so on. In case of an odd
101/// number of bytes, the middle byte will stay untouched.
102///
103/// ## Safety
104///
105/// The caller must guarantee that `T` remains valid after all bytes were
106/// copied and swapped.
107pub const unsafe fn bswap_copy<T>(v: &T) -> T {
108    // SAFETY: The caller guarantees that `T` is valid with all bytes swapped.
109    //         And due to `T: Copy`, we can safely create memory copies.
110    unsafe {
111        match core::mem::size_of::<T>() {
112            1 => transmute_copy(v),
113            2 => transmute_copy(&u16::swap_bytes(transmute_copy(v))),
114            4 => transmute_copy(&u32::swap_bytes(transmute_copy(v))),
115            8 => transmute_copy(&u64::swap_bytes(transmute_copy(v))),
116            16 => transmute_copy(&u128::swap_bytes(transmute_copy(v))),
117            _ => bswap_slow(v),
118        }
119    }
120}
121
122/// Alias a type as a byte slice.
123///
124/// This function allows accessing any type as a slice of bytes. This is safe
125/// for all types. However, the content of padding bytes is neither well
126/// defined nor stable.
127pub const fn as_bytes<'a, T>(v: &'a T) -> &'a [u8] {
128    // SAFETY: We retain the allocation size of `T` and its lifetime. Hence,
129    //         the transmute is safe for as long as `'a`. Since `v` is
130    //         borrowed, we prevent mutable access for the entire lifetime of
131    //         the returned value.
132    unsafe {
133        core::slice::from_raw_parts::<'a, u8>(
134            v as *const _ as *const _,
135            core::mem::size_of::<T>(),
136        )
137    }
138}
139
140/// Alias a type as a mutable byte slice.
141///
142/// This function allows accessing any type as a mutable slice of bytes. This
143/// is inherently unsafe, as it allows modifying the backing memory of any type
144/// and, thus, might violate type invariants.
145///
146/// ## Safety
147///
148/// Like [`as_bytes()`], this can be safely called on any type. However, unlike
149/// [`as_bytes()`], this function grants mutable access and thus any mutable
150/// use of the returned reference must guarantee not to violate any invariants
151/// of `T`.
152pub const unsafe fn as_bytes_mut<'a, T>(v: &'a mut T) -> &'a mut [u8] {
153    // SAFETY: We retain the allocation size of `T` and its lifetime. Hence,
154    //         the transmute is safe for as long as `'a`. Since `v` is
155    //         borrowed, we claim mutable access for the entire lifetime of
156    //         the returned value.
157    unsafe {
158        core::slice::from_raw_parts_mut::<'a, u8>(
159            v as *mut _ as *mut _,
160            core::mem::size_of::<T>(),
161        )
162    }
163}
164
165/// Compare backing memory for equality.
166///
167/// Compare the backing memory of two values for equality. Return `true` if the
168/// memory compares equal, false if not. Note that all memory must compare
169/// equal, including padding bytes (which have no guaranteed nor stable value).
170pub fn eq<A, B>(a: &A, b: &B) -> bool {
171    *as_bytes(a) == *as_bytes(b)
172}
173
174#[cfg(test)]
175mod test {
176    use super::*;
177
178    // Verify `transmute_copy_uninit()` works in constant contexts.
179    #[test]
180    fn transmute_copy_uninit_const() {
181        const C: u16 = unsafe { transmute_copy_uninit(&71u32) };
182
183        assert_eq!(C, 71);
184    }
185
186    // Verify `transmute_copy_uninit()` works with padded/truncated
187    // destinations.
188    #[test]
189    fn transmute_copy_uninit_mismatch() {
190        #[derive(Clone, Copy)]
191        #[repr(align(4))]
192        struct Overaligned {
193            v: u16,
194        }
195
196        #[derive(Clone, Copy)]
197        #[repr(packed)]
198        struct Underaligned {
199            v: u16,
200        }
201
202        assert_eq!(core::mem::size_of::<Overaligned>(), 4);
203        assert_eq!(core::mem::align_of::<Overaligned>(), 4);
204        assert_eq!(core::mem::size_of::<Underaligned>(), 2);
205        assert_eq!(core::mem::align_of::<Underaligned>(), 1);
206
207        let s: u16 = 71;
208        let o: Overaligned = unsafe { transmute_copy_uninit(&s) };
209        let or: u16 = unsafe { transmute_copy_uninit(&o) };
210        let u: Underaligned = unsafe { transmute_copy_uninit(&s) };
211        let ur: u16 = unsafe { transmute_copy_uninit(&u) };
212
213        assert_eq!(s, 71);
214        assert_eq!(o.v, 71);
215        assert_eq!(or, 71);
216        assert_eq!(u.v as u16, 71); // prevent the macro from creating a ref
217        assert_eq!(ur, 71);
218    }
219
220    // Verify basic byte swapping
221    #[test]
222    fn bswap_basic() {
223        unsafe {
224            assert_eq!(bswap_copy(&0x12u8), 0x12u8);
225            assert_eq!(bswap_copy(&0x1234u16), 0x3412u16);
226            assert_eq!(bswap_copy(&0x12345678u32), 0x78563412u32);
227            assert_eq!(bswap_copy(&0x0011223344556677u64), 0x7766554433221100u64);
228            assert_eq!(bswap_copy(&0x00112233445566778899101112131415u128), 0x15141312111099887766554433221100u128);
229            assert_eq!(bswap_copy(&[0x00u8, 0x11u8, 0x22u8]), [0x22u8, 0x11u8, 0x00u8]);
230        }
231    }
232
233    // Verify byte aliasing
234    #[test]
235    fn byte_alias() {
236        let mut v: u16 = 0xf0f0;
237
238        assert_eq!(as_bytes(&v)[0], 0xf0);
239        assert_eq!(as_bytes(&v)[1], 0xf0);
240
241        unsafe {
242            as_bytes_mut(&mut v)[0] = 0x0f;
243            as_bytes_mut(&mut v)[1] = 0x0f;
244        }
245
246        assert_eq!(as_bytes(&v)[0], 0x0f);
247        assert_eq!(as_bytes(&v)[1], 0x0f);
248
249        assert_eq!(v, 0x0f0f);
250    }
251
252    // Verify byte-wise equality
253    #[test]
254    fn byte_eq() {
255        let v: u16 = 0xf0f0;
256
257        assert!(eq(&v, &0xf0f0u16));
258        assert!(eq(&v, &[0xf0u8, 0xf0u8]));
259
260        assert!(!eq(&v, &0x00f0u16));
261        assert!(!eq(&v, &0xf0f0u32));
262        assert!(!eq(&v, &[0xf0u16, 0xf0u16]));
263    }
264}