Skip to content

Commit a44bdcc

Browse files
committed
rust: types: introduce FromBytes 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 `FromBytes` for simple structs whose fields all implement `FromBytes` as well. Signed-off-by: Wedson Almeida Filho <[email protected]>
1 parent ca4a93c commit a44bdcc

File tree

1 file changed

+100
-1
lines changed

1 file changed

+100
-1
lines changed

rust/kernel/types.rs

Lines changed: 100 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
};
@@ -463,3 +463,102 @@ impl<T: LittleEndian + Copy> core::convert::From<T> for LE<T> {
463463
LE(value.to_le())
464464
}
465465
}
466+
467+
/// Specifies that a type is safely readable from byte slices.
468+
///
469+
/// Not all types can be safely read from byte slices; examples from
470+
/// <https://doc.rust-lang.org/reference/behavior-considered-undefined.html> include [`bool`] that
471+
/// must be either `0` or `1`, and [`char`] that cannot be a surrogate or above [`char::MAX`].
472+
///
473+
/// # Safety
474+
///
475+
/// Implementers must ensure that any bit pattern is valid for this type.
476+
pub unsafe trait FromBytes: Sized {
477+
/// Converts the given byte slice into a shared reference to [`Self`].
478+
///
479+
/// It fails if the size or alignment requirements are not satisfied.
480+
fn from_bytes(data: &[u8], offset: usize) -> Option<&Self> {
481+
if offset > data.len() {
482+
return None;
483+
}
484+
let data = &data[offset..];
485+
let ptr = data.as_ptr();
486+
if ptr as usize % align_of::<Self>() != 0 || data.len() < size_of::<Self>() {
487+
return None;
488+
}
489+
// SAFETY: The memory is valid for read because we have a reference to it. We have just
490+
// checked the minimum size and alignment as well.
491+
Some(unsafe { &*ptr.cast() })
492+
}
493+
494+
/// Converts the given byte slice into a shared slice of [`Self`].
495+
///
496+
/// It fails if the size or alignment requirements are not satisfied.
497+
fn from_bytes_to_slice(data: &[u8]) -> Option<&[Self]> {
498+
let ptr = data.as_ptr();
499+
if ptr as usize % align_of::<Self>() != 0 {
500+
return None;
501+
}
502+
// SAFETY: The memory is valid for read because we have a reference to it. We have just
503+
// checked the minimum alignment as well, and the length of the slice is calculated from
504+
// the length of `Self`.
505+
Some(unsafe { core::slice::from_raw_parts(ptr.cast(), data.len() / size_of::<Self>()) })
506+
}
507+
}
508+
509+
// SAFETY: All bit patterns are acceptable values of the types below.
510+
unsafe impl FromBytes for u8 {}
511+
unsafe impl FromBytes for u16 {}
512+
unsafe impl FromBytes for u32 {}
513+
unsafe impl FromBytes for u64 {}
514+
unsafe impl FromBytes for usize {}
515+
unsafe impl FromBytes for i8 {}
516+
unsafe impl FromBytes for i16 {}
517+
unsafe impl FromBytes for i32 {}
518+
unsafe impl FromBytes for i64 {}
519+
unsafe impl FromBytes for isize {}
520+
unsafe impl<const N: usize, T: FromBytes> FromBytes for [T; N] {}
521+
unsafe impl<T: FromBytes + Copy + LittleEndian> FromBytes for LE<T> {}
522+
523+
/// Derive [`FromBytes`] for the structs defined in the block.
524+
///
525+
/// # Examples
526+
///
527+
/// ```
528+
/// kernel::derive_readable_from_bytes! {
529+
/// #[repr(C)]
530+
/// struct SuperBlock {
531+
/// a: u16,
532+
/// _padding: [u8; 6],
533+
/// b: u64,
534+
/// }
535+
///
536+
/// #[repr(C)]
537+
/// struct Inode {
538+
/// a: u16,
539+
/// b: u16,
540+
/// c: u32,
541+
/// }
542+
/// }
543+
/// ```
544+
#[macro_export]
545+
macro_rules! derive_readable_from_bytes {
546+
($($(#[$outer:meta])* $outerv:vis struct $name:ident {
547+
$($(#[$m:meta])* $v:vis $id:ident : $t:ty),* $(,)?
548+
})*)=> {
549+
$(
550+
$(#[$outer])*
551+
$outerv struct $name {
552+
$(
553+
$(#[$m])*
554+
$v $id: $t,
555+
)*
556+
}
557+
unsafe impl $crate::types::FromBytes for $name {}
558+
const _: () = {
559+
const fn is_readable_from_bytes<T: $crate::types::FromBytes>() {}
560+
$(is_readable_from_bytes::<$t>();)*
561+
};
562+
)*
563+
};
564+
}

0 commit comments

Comments
 (0)