@@ -4,7 +4,7 @@ use super::Revision;
44use crate :: table:: boot:: MemoryDescriptor ;
55use crate :: { CStr16 , Error , Result , Status , StatusExt } ;
66use core:: fmt:: { self , Debug , Display , Formatter } ;
7- use core:: mem:: MaybeUninit ;
7+ use core:: mem:: { size_of , MaybeUninit } ;
88use core:: ptr;
99
1010pub use uefi_raw:: capsule:: { CapsuleBlockDescriptor , CapsuleFlags , CapsuleHeader } ;
@@ -582,6 +582,69 @@ impl Display for Time {
582582 }
583583}
584584
585+ /// Error returned from failing to convert a byte slice into a [`Time`].
586+ #[ derive( Clone , Copy , Debug , PartialEq , Eq ) ]
587+ pub enum TimeByteConversionError {
588+ /// One or more fields of the converted [`Time`] is invalid.
589+ InvalidFields ( TimeError ) ,
590+ /// The byte slice is not large enough to hold a [`Time`].
591+ InvalidSize ,
592+ }
593+
594+ impl Display for TimeByteConversionError {
595+ fn fmt ( & self , f : & mut Formatter ) -> fmt:: Result {
596+ match self {
597+ Self :: InvalidFields ( error) => write ! ( f, "{error}" ) ,
598+ Self :: InvalidSize => write ! (
599+ f,
600+ "the byte slice is not large enough to hold a Time struct"
601+ ) ,
602+ }
603+ }
604+ }
605+
606+ impl TryFrom < & [ u8 ] > for Time {
607+ type Error = TimeByteConversionError ;
608+
609+ fn try_from ( bytes : & [ u8 ] ) -> core:: result:: Result < Self , Self :: Error > {
610+ if size_of :: < Time > ( ) <= bytes. len ( ) {
611+ let year = u16:: from_le_bytes ( bytes[ 0 ..2 ] . try_into ( ) . unwrap ( ) ) ;
612+ let month = bytes[ 2 ] ;
613+ let day = bytes[ 3 ] ;
614+ let hour = bytes[ 4 ] ;
615+ let minute = bytes[ 5 ] ;
616+ let second = bytes[ 6 ] ;
617+ let nanosecond = u32:: from_le_bytes ( bytes[ 8 ..12 ] . try_into ( ) . unwrap ( ) ) ;
618+ let time_zone = match i16:: from_le_bytes ( bytes[ 12 ..14 ] . try_into ( ) . unwrap ( ) ) {
619+ Self :: UNSPECIFIED_TIMEZONE => None ,
620+ num => Some ( num) ,
621+ } ;
622+ let daylight = Daylight :: from_bits ( bytes[ 14 ] ) . ok_or (
623+ TimeByteConversionError :: InvalidFields ( TimeError {
624+ daylight : true ,
625+ ..Default :: default ( )
626+ } ) ,
627+ ) ?;
628+
629+ let time_params = TimeParams {
630+ year,
631+ month,
632+ day,
633+ hour,
634+ minute,
635+ second,
636+ nanosecond,
637+ time_zone,
638+ daylight,
639+ } ;
640+
641+ Time :: new ( time_params) . map_err ( TimeByteConversionError :: InvalidFields )
642+ } else {
643+ Err ( TimeByteConversionError :: InvalidSize )
644+ }
645+ }
646+ }
647+
585648/// Unique key for a variable.
586649#[ cfg( feature = "alloc" ) ]
587650#[ derive( Debug ) ]
@@ -651,3 +714,86 @@ pub struct CapsuleInfo {
651714 /// The type of reset required for the capsule update.
652715 pub reset_type : ResetType ,
653716}
717+
718+ #[ cfg( test) ]
719+ mod tests {
720+ use super :: * ;
721+
722+ use alloc:: string:: ToString ;
723+ use core:: { slice, usize} ;
724+
725+ unsafe fn time_as_u8_slice ( p : & Time ) -> & [ u8 ] {
726+ slice:: from_raw_parts ( core:: ptr:: addr_of!( * p) . cast ( ) , size_of :: < Time > ( ) )
727+ }
728+
729+ unsafe fn time_as_u8_slice_with_size ( p : & Time , len : usize ) -> & [ u8 ] {
730+ slice:: from_raw_parts ( core:: ptr:: addr_of!( * p) . cast ( ) , len)
731+ }
732+
733+ #[ test]
734+ fn test_successful_time_from_bytes ( ) {
735+ let mut time;
736+ let mut time_from_bytes;
737+ let mut time_params = TimeParams {
738+ year : 2024 ,
739+ month : 6 ,
740+ day : 13 ,
741+ hour : 4 ,
742+ minute : 29 ,
743+ second : 30 ,
744+ nanosecond : 123_456_789 ,
745+ time_zone : None ,
746+ daylight : Daylight :: empty ( ) ,
747+ } ;
748+
749+ time = Time :: new ( time_params) . unwrap ( ) ;
750+ unsafe {
751+ time_from_bytes = Time :: try_from ( time_as_u8_slice ( & time) ) . unwrap ( ) ;
752+ }
753+ assert_eq ! ( time, time_from_bytes) ;
754+
755+ time_params. time_zone = Some ( 120 ) ;
756+ time = Time :: new ( time_params) . unwrap ( ) ;
757+ unsafe {
758+ time_from_bytes = Time :: try_from ( time_as_u8_slice ( & time) ) . unwrap ( ) ;
759+ }
760+ assert_eq ! ( time. to_string( ) , time_from_bytes. to_string( ) ) ;
761+
762+ time_params. time_zone = Some ( 150 ) ;
763+ time = Time :: new ( time_params) . unwrap ( ) ;
764+ unsafe {
765+ time_from_bytes = Time :: try_from ( time_as_u8_slice ( & time) ) . unwrap ( ) ;
766+ }
767+ assert_eq ! ( time. to_string( ) , time_from_bytes. to_string( ) ) ;
768+ }
769+
770+ #[ test]
771+ fn test_invalid_fields_in_time_byte_conversion ( ) {
772+ let time = Time :: invalid ( ) ;
773+ let time_from_bytes;
774+ unsafe {
775+ time_from_bytes = Time :: try_from ( time_as_u8_slice ( & time) ) . unwrap_err ( ) ;
776+ }
777+ assert_eq ! (
778+ TimeByteConversionError :: InvalidFields ( TimeError {
779+ year: true ,
780+ month: true ,
781+ day: true ,
782+ ..Default :: default ( )
783+ } ) ,
784+ time_from_bytes
785+ ) ;
786+ }
787+
788+ #[ test]
789+ fn test_byte_slice_too_small_to_convert_to_time ( ) {
790+ let time = Time :: invalid ( ) ;
791+ let time_from_bytes;
792+ unsafe {
793+ time_from_bytes =
794+ Time :: try_from ( time_as_u8_slice_with_size ( & time, size_of :: < Time > ( ) - 1 ) )
795+ . unwrap_err ( ) ;
796+ }
797+ assert_eq ! ( TimeByteConversionError :: InvalidSize , time_from_bytes) ;
798+ }
799+ }
0 commit comments