@@ -7,7 +7,7 @@ use alloc::boxed::Box;
77use 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