Skip to content

Commit c000dcc

Browse files
committed
[WIP] TryFromBytes
TODO: How to support `project!` when `Projectable` is implemented multiple times for `ByteArray`, `Align`, and `AlignedByteArray`? If we only emit an impl for `AlignedByteArray`, everything is fine, but once we emit the other two, type inference fails. Makes progress on #5
1 parent 865a9b5 commit c000dcc

File tree

2 files changed

+241
-27
lines changed

2 files changed

+241
-27
lines changed

src/derive_util.rs

+26
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,29 @@ 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+
#[doc(hidden)] // `#[macro_export]` bypasses this module's `#[doc(hidden)]`.
70+
#[macro_export]
71+
macro_rules! impl_try_from_bytes {
72+
($ty:ty { $($f:tt: $f_ty:ty),* } $(=> $validation_method:ident)?) => {
73+
#[allow(unused_qualifications)]
74+
unsafe impl zerocopy::TryFromBytes for $ty {
75+
fn is_bit_valid(bytes: &zerocopy::MaybeValid<Self>) -> bool {
76+
true $(&& {
77+
let f: &zerocopy::MaybeValid<$f_ty>
78+
= zerocopy::derive_util::__project!(&bytes.$f);
79+
zerocopy::TryFromBytes::is_bit_valid(f)
80+
})*
81+
$(&& {
82+
let bytes_ptr: *const zerocopy::MaybeValid<Self> = bytes;
83+
let slf = unsafe { &*bytes_ptr.cast::<Self>() };
84+
// TODO: What about interior mutability?
85+
slf.$validation_method()
86+
})?
87+
}
88+
}
89+
}
90+
}

src/lib.rs

+215-27
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,215 @@ pub unsafe trait FromBytes: FromZeroes {
502502
}
503503
}
504504

505+
/// TODO
506+
///
507+
/// # Safety
508+
///
509+
/// `AsMaybeUninit` must only be implemented for types which are `Sized` or
510+
/// whose last field is a slice whose element type is `Sized` (this includes
511+
/// slice types themselves; in a slice type, the "last field" simply refers to
512+
/// the slice itself).
513+
pub unsafe trait AsMaybeUninit {
514+
/// TODO
515+
///
516+
/// # Safety
517+
///
518+
/// For `T: AsMaybeUninit`, the following must hold:
519+
/// - Given `m: T::MaybeUninit`, it is sound to write uninitialized bytes at
520+
/// every byte offset in `m` (this description avoids the "what lengths
521+
/// are valid" problem)
522+
/// - `T` and `T::MaybeUninit` have the same alignment requirement (can't
523+
/// use `align_of` to describe this because it requires that its argument
524+
/// is sized)
525+
/// - `T` and `T::MaybeUninit` are either both `Sized` or both `!Sized`
526+
/// - If they are `Sized`, `size_of::<T>() == size_of::<T::MaybeUninit>()`
527+
/// - If they are `!Sized`, then they are both DSTs with a trailing slice.
528+
/// Given `t: &T` and `m: &T::MaybeUninit` with the same number of
529+
/// elements in their trailing slices, `size_of_val(t) == size_of_val(m)`.
530+
type MaybeUninit: ?Sized;
531+
}
532+
533+
unsafe impl<T: Sized> AsMaybeUninit for T {
534+
type MaybeUninit = MaybeUninit<T>;
535+
}
536+
537+
unsafe impl<T: Sized> AsMaybeUninit for [T] {
538+
type MaybeUninit = [MaybeUninit<T>];
539+
}
540+
541+
/// A value which might or might not constitute a valid instance of `T`.
542+
///
543+
/// `MaybeValid<T>` has the same layout (size and alignment) and field offsets
544+
/// as `T`. However, it may contain any bit pattern with a few restrictions:
545+
/// Given `m: MaybeValid<T>` and a byte offset, `b` in the range `[0,
546+
/// size_of::<MaybeValid<T>>())`:
547+
/// - If, in all valid instances `t: T`, the byte at offset `b` in `t` is
548+
/// initialized, then the byte at offset `b` within `m` is guaranteed to be
549+
/// initialized.
550+
/// - Let `s` be the sequence of bytes of length `b` in the offset range `[0,
551+
/// b)` in `m`. Let `TT` be the subset of valid instances of `T` which contain
552+
/// this sequence in the offset range `[0, b)`. If, for all instances of `t:
553+
/// T` in `TT`, the byte at offset `b` in `t` is initialized, then the byte at
554+
/// offset `b` in `m` is guaranteed to be initialized.
555+
///
556+
/// Pragmatically, this means that if `m` is guaranteed to contain an enum
557+
/// type at a particular offset, and the enum discriminant stored in `m`
558+
/// corresponds to a valid variant of that enum type, then it is guaranteed
559+
/// that the appropriate bytes of `m` are initialized as defined by that
560+
/// variant's layout (although note that the variant's layout may contain
561+
/// another enum type, in which case the same rules apply depending on the
562+
/// state of its discriminant, and so on recursively).
563+
///
564+
/// # Safety
565+
///
566+
/// TODO (make sure to mention enum layout)
567+
#[repr(transparent)]
568+
pub struct MaybeValid<T: AsMaybeUninit + ?Sized> {
569+
inner: T::MaybeUninit,
570+
}
571+
572+
impl<T> MaybeValid<T> {
573+
/// TODO
574+
pub const unsafe fn assume_valid(self) -> T {
575+
unsafe { self.inner.assume_init() }
576+
}
577+
}
578+
579+
impl<T> MaybeValid<[T]> {
580+
/// TODO
581+
pub const fn as_slice_of_maybe_valids(&self) -> &[MaybeValid<T>] {
582+
let inner: &[<T as AsMaybeUninit>::MaybeUninit] = &self.inner;
583+
// SAFETY: Since `inner` is a `&[T::MaybeUninit]`, and `MaybeValid<T>`
584+
// is a `repr(transparent)` struct around `T::MaybeUninit`, `inner` has
585+
// the same layout as `&[MaybeValid<T>]`.
586+
unsafe { mem::transmute(inner) }
587+
}
588+
}
589+
590+
unsafe impl<T, F> Projectable<F, MaybeValid<F>> for MaybeValid<T> {
591+
type Inner = T;
592+
}
593+
594+
impl<T> From<ByteArray<T>> for MaybeValid<T> {
595+
fn from(bytes: ByteArray<T>) -> MaybeValid<T> {
596+
todo!()
597+
}
598+
}
599+
600+
impl<'a, T> From<&'a Align<ByteArray<T>, T>> for &'a MaybeValid<T> {
601+
fn from(bytes: &'a Align<ByteArray<T>, T>) -> &'a MaybeValid<T> {
602+
todo!()
603+
}
604+
}
605+
606+
impl<'a, T> From<&'a mut Align<ByteArray<T>, T>> for &'a mut MaybeValid<T> {
607+
fn from(bytes: &'a mut Align<ByteArray<T>, T>) -> &'a mut MaybeValid<T> {
608+
todo!()
609+
}
610+
}
611+
612+
impl<T> Debug for MaybeValid<T> {
613+
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
614+
f.pad(core::any::type_name::<Self>())
615+
}
616+
}
617+
618+
/// TODO
619+
pub unsafe trait TryFromBytes: AsMaybeUninit {
620+
/// TODO
621+
fn is_bit_valid(candidate: &MaybeValid<Self>) -> bool;
622+
623+
/// TODO
624+
// Note that, in a future in which we distinguish between `FromBytes` and `RefFromBytes`,
625+
// this requires `where Self: RefFromBytes` to disallow interior mutability.
626+
fn try_from_ref(bytes: &[u8]) -> Option<&Self>
627+
where
628+
// TODO: Remove this bound.
629+
Self: Sized,
630+
{
631+
let byte_array: &ByteArray<Self> = TryFrom::try_from(bytes).ok()?;
632+
let aligned = Align::try_from_ref(byte_array)?;
633+
634+
if Self::is_bit_valid(aligned.into()) {
635+
Some(unsafe { &*bytes.as_ptr().cast::<Self>() })
636+
} else {
637+
None
638+
}
639+
}
640+
641+
/// TODO
642+
fn try_from_mut(bytes: &mut [u8]) -> Option<&mut Self>
643+
where
644+
// TODO: Remove the `Sized` bound.
645+
Self: AsBytes + Sized,
646+
{
647+
let byte_array: &ByteArray<Self> = TryFrom::try_from(&*bytes).ok()?;
648+
let aligned = Align::try_from_ref(byte_array)?;
649+
650+
if Self::is_bit_valid(aligned.into()) {
651+
Some(unsafe { &mut *bytes.as_mut_ptr().cast::<Self>() })
652+
} else {
653+
None
654+
}
655+
}
656+
657+
/// TODO
658+
fn try_read_from(bytes: &[u8]) -> Option<Self>
659+
where
660+
Self: Sized,
661+
// TODO: Why can't Rust infer this based on the blanket impl for `T:
662+
// Sized`? It sets `MaybeUninit = MaybeUninit<T>`, which is `Sized`.
663+
<Self as AsMaybeUninit>::MaybeUninit: Sized,
664+
{
665+
let byte_array: &ByteArray<Self> = TryFrom::try_from(bytes).ok()?;
666+
let maybe_valid = MaybeValid::from(byte_array.clone());
667+
668+
if Self::is_bit_valid(&maybe_valid) {
669+
Some(unsafe { maybe_valid.assume_valid() })
670+
} else {
671+
None
672+
}
673+
}
674+
}
675+
676+
unsafe impl<T: FromBytes> TryFromBytes for T {
677+
fn is_bit_valid(_candidate: &MaybeValid<T>) -> bool {
678+
true
679+
}
680+
}
681+
682+
unsafe impl<T: TryFromBytes + Sized> TryFromBytes for [T]
683+
where
684+
// TODO: Why can't Rust infer this based on the blanket impl for `T: Sized`?
685+
// It sets `MaybeUninit = MaybeUninit<T>`, which is `Sized`.
686+
<T as AsMaybeUninit>::MaybeUninit: Sized,
687+
{
688+
fn is_bit_valid(candidate: &MaybeValid<[T]>) -> bool {
689+
candidate.as_slice_of_maybe_valids().iter().all(|c| T::is_bit_valid(c))
690+
}
691+
}
692+
693+
mod try_from_bytes_derive_example {
694+
use super::*;
695+
696+
struct Foo {
697+
a: u8,
698+
b: u16,
699+
}
700+
701+
impl_try_from_bytes!(Foo { a: u8, b: u16 });
702+
703+
struct Bar(Foo);
704+
705+
impl Bar {
706+
fn is_valid(&self) -> bool {
707+
u16::from(self.0.a) < self.0.b
708+
}
709+
}
710+
711+
impl_try_from_bytes!(Bar { 0: Foo } => is_valid);
712+
}
713+
505714
/// Types which are safe to treat as an immutable byte slice.
506715
///
507716
/// WARNING: Do not implement this trait yourself! Instead, use
@@ -1543,21 +1752,9 @@ impl<T: Display + ?Sized, A> Display for Align<T, A> {
15431752
}
15441753
}
15451754

1546-
unsafe impl<T: ?Sized, A> Projectable for Align<T, A> {
1547-
type Inner = T;
1548-
// SAFETY: We know that `U` can't be more aligned than `T` or else it
1549-
// couldn't be a field in `T`. Thus, any `U` within `Align<T, A>` is already
1550-
// aligned to `max(align_of::<U>(), align_of::<A>())`.
1551-
type Wrapped<U> = Align<U, A>;
1552-
1553-
fn foo(&self) -> *const T {
1554-
self as *const Self as *const T
1555-
}
1556-
1557-
fn foo_mut(&mut self) -> *mut T {
1558-
self as *mut Self as *mut T
1559-
}
1560-
}
1755+
// unsafe impl<T: ?Sized, F: ?Sized, A> Projectable<F, Align<F, A>> for Align<T, A> {
1756+
// type Inner = T;
1757+
// }
15611758

15621759
/// A type with no alignment requirement.
15631760
///
@@ -2171,18 +2368,9 @@ impl<T> Ord for ByteArray<T> {
21712368
}
21722369
}
21732370

2174-
unsafe impl<T> Projectable for ByteArray<T> {
2175-
type Inner = T;
2176-
type Wrapped<U> = ByteArray<U>;
2177-
2178-
fn foo(&self) -> *const T {
2179-
self as *const Self as *const T
2180-
}
2181-
2182-
fn foo_mut(&mut self) -> *mut T {
2183-
self as *mut Self as *mut T
2184-
}
2185-
}
2371+
// unsafe impl<T, F> Projectable<F, ByteArray<F>> for ByteArray<T> {
2372+
// type Inner = T;
2373+
// }
21862374

21872375
// Used in `transmute!` below.
21882376
#[doc(hidden)]

0 commit comments

Comments
 (0)