Skip to content

Commit 9a96c8a

Browse files
committed
[WIP] TryFromBytes
TODO: - Finish writing safety comments - Finish writing documentation - Add tests Makes progress on #5
1 parent 63ae053 commit 9a96c8a

File tree

3 files changed

+577
-55
lines changed

3 files changed

+577
-55
lines changed

src/derive_util.rs

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

0 commit comments

Comments
 (0)