Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions lib/osi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,14 @@

#![no_std]

extern crate alloc;
extern crate core;

#[cfg(any(test, feature = "std"))]
extern crate std;

// Used by macros via `$crate::{alloc,core}::*`, explicitly part of the public
// API. Usually of little use to code outside of this crate, though.
pub extern crate alloc;
pub extern crate core;

pub mod align;
pub mod args;
pub mod brand;
Expand Down
120 changes: 85 additions & 35 deletions lib/osi/src/mem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,19 @@
//! This module contains functions to help dealing with direct memory
//! manipulation and inspection.

use core::mem::MaybeUninit as Uninit;
use core::mem::transmute_copy;

#[doc(hidden)]
#[macro_export]
macro_rules! crate_mem_typed_offset_of {
($container:ty, $field:ident, $ty:ty $(,)?) => {
const {
let v = ::core::mem::MaybeUninit::<$container>::uninit();
let v = $crate::core::mem::MaybeUninit::<$container>::uninit();
let _: *const $ty = unsafe {
&raw const ((*v.as_ptr()).$field)
};
::core::mem::offset_of!($container, $field)
$crate::core::mem::offset_of!($container, $field)
}
}
}
Expand Down Expand Up @@ -85,7 +86,7 @@ pub const unsafe fn transmute_copy_uninit<Src, Dst>(src: &Src) -> Dst {
//
// SAFETY: Delegated to the caller.
unsafe {
let mut dst = core::mem::MaybeUninit::<Dst>::uninit();
let mut dst = Uninit::<Dst>::uninit();
copy_unaligned(
src as *const Src,
dst.as_mut_ptr() as *mut Src,
Expand Down Expand Up @@ -114,7 +115,7 @@ pub const unsafe fn transmute_copy_uninit<Src, Dst>(src: &Src) -> Dst {
// This is the slow-path that manually iterates the individual bytes, but works
// with any data-type, as long as the data-type stays valid with swapped bytes.
const unsafe fn bswap_slow<T>(v: &T) -> T {
let mut r = core::mem::MaybeUninit::<T>::uninit();
let mut r = Uninit::<T>::uninit();
let src = v as *const T as *const u8;
let dst = r.as_mut_ptr() as *mut u8;

Expand Down Expand Up @@ -159,16 +160,38 @@ pub const unsafe fn bswap_copy<T>(v: &T) -> T {
}
}

/// Alias a type as a [`MaybeUninit`](core::mem::MaybeUninit).
///
/// Any type can be aliased as a possibly uninitialized type. This is usually
/// only necessary when providing initialized data to code that can handle
/// possibly uninitialized data.
pub const fn as_uninit<'a, T>(v: &'a T) -> &'a Uninit<T> {
unsafe {
core::mem::transmute::<&T, &Uninit<T>>(v)
}
}

/// Alias a slice as a [`MaybeUninit`](core::mem::MaybeUninit).
///
/// This works like [`as_uninit()`] but for slices of `T`.
pub const fn slice_as_uninit<'a, T>(v: &'a [T]) -> &'a [Uninit<T>] {
unsafe {
core::mem::transmute::<&[T], &[Uninit<T>]>(v)
}
}

/// Alias a type as a byte slice.
///
/// This function allows accessing any type as a slice of bytes. This is safe
/// for all types. However, the content of padding bytes is neither well
/// defined nor stable.
pub const fn as_bytes<'a, T>(v: &'a T) -> &'a [u8] {
// SAFETY: We retain the allocation size of `T` and its lifetime. Hence,
// the transmute is safe for as long as `'a`. Since `v` is
// borrowed, we prevent mutable access for the entire lifetime of
// the returned value.
/// This function allows accessing any type as a slice of bytes.
///
/// ## Safety
///
/// The caller must guarantee that:
///
/// - `T` has no padding bytes, and all bytes of `v` belong to the type `T.
/// This implies that all bytes in `v` are initialized.
pub const unsafe fn as_bytes<'a, T>(v: &'a T) -> &'a [u8] {
// SAFETY: Propagated to caller.
unsafe {
core::slice::from_raw_parts::<'a, u8>(
v as *const _ as *const _,
Expand All @@ -185,15 +208,15 @@ pub const fn as_bytes<'a, T>(v: &'a T) -> &'a [u8] {
///
/// ## Safety
///
/// Like [`as_bytes()`], this can be safely called on any type. However, unlike
/// [`as_bytes()`], this function grants mutable access and thus any mutable
/// use of the returned reference must guarantee not to violate any invariants
/// of `T`.
/// The caller must guarantee that:
///
/// - `T` has no padding bytes, and all bytes of `v` belong to the type `T.
/// This implies that all bytes in `v` are initialized.
/// - Any bit sequence in `v` produces a valid value of type `T`. This
/// guarantees that no use of the returned reference ever produces invalid
/// values.
pub const unsafe fn as_bytes_mut<'a, T>(v: &'a mut T) -> &'a mut [u8] {
// SAFETY: We retain the allocation size of `T` and its lifetime. Hence,
// the transmute is safe for as long as `'a`. Since `v` is
// borrowed, we claim mutable access for the entire lifetime of
// the returned value.
// SAFETY: Propagated to caller.
unsafe {
core::slice::from_raw_parts_mut::<'a, u8>(
v as *mut _ as *mut _,
Expand All @@ -206,9 +229,18 @@ pub const unsafe fn as_bytes_mut<'a, T>(v: &'a mut T) -> &'a mut [u8] {
///
/// Compare the backing memory of two values for equality. Return `true` if the
/// memory compares equal, false if not. Note that all memory must compare
/// equal, including padding bytes (which have no guaranteed nor stable value).
pub fn eq<A, B>(a: &A, b: &B) -> bool {
*as_bytes(a) == *as_bytes(b)
/// equal.
///
/// ## Safety
///
/// The caller must guarantee that:
///
/// - `A` and `B` have no padding bytes, and all bytes of `a` and `b` belong to
/// their respective type. This implies that all bytes in `a` and `b` are
/// initialized.
pub unsafe fn eq<A, B>(a: &A, b: &B) -> bool {
// SAFETY: Propagated to caller.
unsafe { *as_bytes(a) == *as_bytes(b) }
}

#[cfg(test)]
Expand Down Expand Up @@ -270,35 +302,53 @@ mod test {
}
}

// Verify uninit aliasing
#[test]
fn uninit_alias() {
let v: u16 = 0xf0f0;

assert_eq!(v, 0xf0f0);
assert_eq!(unsafe { *as_uninit(&v).as_ptr() }, 0xf0f0);

let v: [u16; 2] = [0xf0, 0xf1];

assert_eq!(v[0], 0xf0);
assert_eq!(v[1], 0xf1);
assert_eq!(unsafe { *slice_as_uninit(&v)[0].as_ptr() }, 0xf0);
assert_eq!(unsafe { *slice_as_uninit(&v)[1].as_ptr() }, 0xf1);
}

// Verify byte aliasing
#[test]
fn byte_alias() {
let mut v: u16 = 0xf0f0;

assert_eq!(as_bytes(&v)[0], 0xf0);
assert_eq!(as_bytes(&v)[1], 0xf0);

unsafe {
assert_eq!(as_bytes(&v)[0], 0xf0);
assert_eq!(as_bytes(&v)[1], 0xf0);

as_bytes_mut(&mut v)[0] = 0x0f;
as_bytes_mut(&mut v)[1] = 0x0f;
}

assert_eq!(as_bytes(&v)[0], 0x0f);
assert_eq!(as_bytes(&v)[1], 0x0f);
assert_eq!(as_bytes(&v)[0], 0x0f);
assert_eq!(as_bytes(&v)[1], 0x0f);

assert_eq!(v, 0x0f0f);
assert_eq!(v, 0x0f0f);
}
}

// Verify byte-wise equality
#[test]
fn byte_eq() {
let v: u16 = 0xf0f0;

assert!(eq(&v, &0xf0f0u16));
assert!(eq(&v, &[0xf0u8, 0xf0u8]));
unsafe {
assert!(eq(&v, &0xf0f0u16));
assert!(eq(&v, &[0xf0u8, 0xf0u8]));

assert!(!eq(&v, &0x00f0u16));
assert!(!eq(&v, &0xf0f0u32));
assert!(!eq(&v, &[0xf0u16, 0xf0u16]));
assert!(!eq(&v, &0x00f0u16));
assert!(!eq(&v, &0xf0f0u32));
assert!(!eq(&v, &[0xf0u16, 0xf0u16]));
}
}
}
20 changes: 13 additions & 7 deletions lib/sys/src/ffi/linux/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,19 @@ use native as libc;

// Compare two `const` definitions for equality. This will compare their type
// layout and memory content for equality.
fn eq_def_const<A, B>(a: &A, b: &B) -> bool {
core::mem::size_of::<A>() == core::mem::size_of::<B>()
&& core::mem::align_of::<A>() == core::mem::align_of::<B>()
&& osi::mem::eq(a, b)
unsafe fn eq_def_const<A, B>(a: &A, b: &B) -> bool {
// SAFETY: Propagated to caller.
unsafe {
core::mem::size_of::<A>() == core::mem::size_of::<B>()
&& core::mem::align_of::<A>() == core::mem::align_of::<B>()
&& osi::mem::eq(a, b)
}
}

// A 3-way variant of `eq_def_const()`.
fn eq3_def_const<A, B, C>(a: &A, b: &B, c: &C) -> bool {
eq_def_const(a, b) && eq_def_const(a, c)
unsafe fn eq3_def_const<A, B, C>(a: &A, b: &B, c: &C) -> bool {
// SAFETY: Propagated to caller.
unsafe { eq_def_const(a, b) && eq_def_const(a, c) }
}

// Verify that all supported platforms are available, by simply checking that
Expand All @@ -37,5 +41,7 @@ fn platform_availability() {
// Compare target APIs with native and libc APIs, and verify they match.
#[test]
fn comparison() {
assert!(eq3_def_const(&target::errno::EPERM, &native::errno::EPERM, &libc::errno::EPERM));
unsafe {
assert!(eq3_def_const(&target::errno::EPERM, &native::errno::EPERM, &libc::errno::EPERM));
}
}