@@ -62,3 +62,68 @@ macro_rules! union_has_padding {
62
62
false $( || core:: mem:: size_of:: <$t>( ) != core:: mem:: size_of:: <$ts>( ) ) *
63
63
} ;
64
64
}
65
+
66
+ /// Implements `TryFromBytes` for a struct type by delegating to existing
67
+ /// implementations for each of its fields, and optionally supports a custom
68
+ /// validation method.
69
+ ///
70
+ /// ```rust
71
+ /// # use zerocopy::impl_try_from_bytes_for_struct;
72
+ ///
73
+ /// #[repr(C)]
74
+ /// struct Foo {
75
+ /// a: u8,
76
+ /// b: u16,
77
+ /// }
78
+ ///
79
+ /// impl_try_from_bytes_for_struct!(Foo { a: u8, b: u16 });
80
+ ///
81
+ /// #[repr(transparent)]
82
+ /// struct Bar(Foo);
83
+ ///
84
+ /// impl Bar {
85
+ /// fn is_valid(&self) -> bool {
86
+ /// u16::from(self.0.a) < self.0.b
87
+ /// }
88
+ /// }
89
+ ///
90
+ /// impl_try_from_bytes_for_struct!(Bar { 0: Foo } => is_valid);
91
+ /// ```
92
+ ///
93
+ /// # Safety
94
+ ///
95
+ /// `$ty` must be a struct type, `$f` must list every field's name, and `$f_ty`
96
+ /// must be the correct types for those fields.
97
+ #[ doc( hidden) ] // `#[macro_export]` bypasses this module's `#[doc(hidden)]`.
98
+ #[ macro_export]
99
+ macro_rules! impl_try_from_bytes_for_struct {
100
+ ( $ty: ty { $( $f: tt: $f_ty: ty) ,* } $( => $validation_method: ident) ?) => {
101
+ // SAFETY: The caller promises that all fields are listed with their
102
+ // correct types. We validate that every field is valid, which is the
103
+ // only requirement for the entire struct to be valid. Thus, we
104
+ // correctly implement `is_bit_valid` as required by the trait's safety
105
+ // documentation.
106
+ #[ allow( unused_qualifications) ]
107
+ unsafe impl zerocopy:: TryFromBytes for $ty {
108
+ fn is_bit_valid( bytes: & zerocopy:: MaybeValid <Self >) -> bool {
109
+ true $( && {
110
+ let f: & zerocopy:: MaybeValid <$f_ty> = zerocopy:: project!( & bytes. $f) ;
111
+ zerocopy:: TryFromBytes :: is_bit_valid( f)
112
+ } ) *
113
+ $( && {
114
+ // SAFETY: We just validated that all of the struct's fields
115
+ // are valid, which means that the struct itself is valid.
116
+ // That is the only precondition of `assume_valid_ref`.
117
+ let slf = unsafe { bytes. assume_valid_ref( ) } ;
118
+ // TODO: What about interior mutability? One approach would
119
+ // be to have the validation method operate on a
120
+ // `#[repr(transparent)]` `Freeze` container that implements
121
+ // `Projectable`. If we eventually get a `Freeze` or
122
+ // `NoCell` trait, that container could implement `Deref`
123
+ // for types which don't contain any cells.
124
+ slf. $validation_method( )
125
+ } ) ?
126
+ }
127
+ }
128
+ }
129
+ }
0 commit comments