Skip to content

Commit f9ef141

Browse files
committed
rust: types: introduce ReadableFromBytes trait
If a type `T` implements this trait, it's possible to safely get a shared reference to a `T` from a byte slice. This commit also provides a macro that allows the automatic derivation of `ReadableFromBytes` for simple structs that whose fields all implement `ReadableFromBytes` as well. Signed-off-by: Wedson Almeida Filho <walmeida@microsoft.com>
1 parent c20e75e commit f9ef141

1 file changed

Lines changed: 94 additions & 1 deletion

File tree

rust/kernel/types.rs

Lines changed: 94 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use alloc::boxed::Box;
77
use core::{
88
cell::UnsafeCell,
99
marker::{PhantomData, PhantomPinned},
10-
mem::MaybeUninit,
10+
mem::{align_of, size_of, MaybeUninit},
1111
ops::{Deref, DerefMut},
1212
ptr::NonNull,
1313
};
@@ -440,3 +440,96 @@ impl<T: LittleEndian + Copy> core::convert::From<T> for LE<T> {
440440
LE(value.to_le())
441441
}
442442
}
443+
444+
/// Specifies that a type is safely readable from byte slices.
445+
///
446+
/// Not all types can be safely read from byte slices; examples from
447+
/// <https://doc.rust-lang.org/reference/behavior-considered-undefined.html> include `bool`
448+
/// that must be either `0` or `1`, and `char` that cannot be a surrogate or above `char::MAX`.
449+
///
450+
/// # Safety
451+
///
452+
/// Implementers must ensure that the type is made up only of types that can be safely read from
453+
/// arbitrary byte sequences (e.g., `u32`, `u64`, etc.).
454+
pub unsafe trait ReadableFromBytes: Sized {
455+
/// Converts the given byte slice into a shared reference to [`Self`].
456+
///
457+
/// It fails if the size or alignment requirements are not satisfied.
458+
fn from_bytes(data: &[u8], offset: usize) -> Option<&Self> {
459+
if offset > data.len() {
460+
return None;
461+
}
462+
let ptr = &data[offset] as *const u8;
463+
if ptr as usize % align_of::<Self>() != 0 || data.len() - offset < size_of::<Self>() {
464+
return None;
465+
}
466+
// SAFETY: The memory is valid for read because we have a reference to it. We have just
467+
// checked the minimum size and alignment as well.
468+
Some(unsafe { &*ptr.cast() })
469+
}
470+
471+
/// Converts the given byte slice into a shared slice of [`Self`].
472+
///
473+
/// It fails if the size or alignment requirements are not satisfied.
474+
fn from_bytes_to_slice(data: &[u8]) -> Option<&[Self]> {
475+
let ptr = &data[0] as *const u8;
476+
if ptr as usize % align_of::<Self>() != 0 {
477+
return None;
478+
}
479+
// SAFETY: The memory is valid for read because we have a reference to it. We have just
480+
// checked the minimum alignment as well, and the length of the slice is calculated from
481+
// the length of `Self`.
482+
Some(unsafe { core::slice::from_raw_parts(ptr.cast(), data.len() / size_of::<Self>()) })
483+
}
484+
}
485+
486+
// SAFETY: All bit patterns are acceptable values of the types below.
487+
unsafe impl ReadableFromBytes for u8 {}
488+
unsafe impl ReadableFromBytes for u16 {}
489+
unsafe impl ReadableFromBytes for u32 {}
490+
unsafe impl ReadableFromBytes for u64 {}
491+
unsafe impl ReadableFromBytes for usize {}
492+
unsafe impl ReadableFromBytes for i8 {}
493+
unsafe impl ReadableFromBytes for i16 {}
494+
unsafe impl ReadableFromBytes for i32 {}
495+
unsafe impl ReadableFromBytes for i64 {}
496+
unsafe impl ReadableFromBytes for isize {}
497+
unsafe impl<T: ReadableFromBytes> ReadableFromBytes for &[T] {}
498+
unsafe impl<const N: usize, T: ReadableFromBytes> ReadableFromBytes for [T; N] {}
499+
unsafe impl<T: ReadableFromBytes + Copy + LittleEndian> ReadableFromBytes for LE<T> {}
500+
501+
/// Derive [`ReadableFromBytes`] for the struct defined.
502+
///
503+
/// # Examples
504+
///
505+
/// ```
506+
/// kernel::derive_readable_from_bytes! {
507+
/// #[repr(C)]
508+
/// struct Inode {
509+
/// a: u16,
510+
/// b: u16,
511+
/// c: u32,
512+
/// }
513+
/// }
514+
/// ```
515+
#[macro_export]
516+
macro_rules! derive_readable_from_bytes {
517+
($($(#[$outer:meta])* $outerv:vis struct $name:ident {
518+
$($(#[$m:meta])* $v:vis $id:ident : $t:ty),* $(,)?
519+
})*)=> {
520+
$(
521+
$(#[$outer])*
522+
$outerv struct $name {
523+
$(
524+
$(#[$m])*
525+
$v $id: $t,
526+
)*
527+
}
528+
unsafe impl $crate::types::ReadableFromBytes for $name {}
529+
const _: () = {
530+
const fn is_readable_from_bytes<T: $crate::types::ReadableFromBytes>() {}
531+
$(is_readable_from_bytes::<$t>();)*
532+
};
533+
)*
534+
};
535+
}

0 commit comments

Comments
 (0)