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}