Skip to content

Commit d4ff259

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 d4ff259

File tree

2 files changed

+222
-27
lines changed

2 files changed

+222
-27
lines changed

src/derive_util.rs

Lines changed: 26 additions & 0 deletions
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

Lines changed: 196 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,196 @@ 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+
unsafe impl<T, F> Projectable<F, MaybeValid<F>> for MaybeValid<T> {
580+
type Inner = T;
581+
}
582+
583+
impl<T> From<ByteArray<T>> for MaybeValid<T> {
584+
fn from(bytes: ByteArray<T>) -> MaybeValid<T> {
585+
todo!()
586+
}
587+
}
588+
589+
impl<'a, T> From<&'a Align<ByteArray<T>, T>> for &'a MaybeValid<T> {
590+
fn from(bytes: &'a Align<ByteArray<T>, T>) -> &'a MaybeValid<T> {
591+
todo!()
592+
}
593+
}
594+
595+
impl<'a, T> From<&'a mut Align<ByteArray<T>, T>> for &'a mut MaybeValid<T> {
596+
fn from(bytes: &'a mut Align<ByteArray<T>, T>) -> &'a mut MaybeValid<T> {
597+
todo!()
598+
}
599+
}
600+
601+
impl<T> Debug for MaybeValid<T> {
602+
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
603+
f.pad(core::any::type_name::<Self>())
604+
}
605+
}
606+
607+
/// TODO
608+
pub unsafe trait TryFromBytes: AsMaybeUninit {
609+
/// TODO
610+
fn is_bit_valid(candidate: &MaybeValid<Self>) -> bool;
611+
612+
/// TODO
613+
// Note that, in a future in which we distinguish between `FromBytes` and `RefFromBytes`,
614+
// this requires `where Self: RefFromBytes` to disallow interior mutability.
615+
fn try_from_ref(bytes: &[u8]) -> Option<&Self>
616+
where
617+
// TODO: Remove this bound.
618+
Self: Sized,
619+
{
620+
let byte_array: &ByteArray<Self> = TryFrom::try_from(bytes).ok()?;
621+
let aligned = Align::try_from_ref(byte_array)?;
622+
623+
if Self::is_bit_valid(aligned.into()) {
624+
Some(unsafe { &*bytes.as_ptr().cast::<Self>() })
625+
} else {
626+
None
627+
}
628+
}
629+
630+
/// TODO
631+
fn try_from_mut(bytes: &mut [u8]) -> Option<&mut Self>
632+
where
633+
// TODO: Remove the `Sized` bound.
634+
Self: AsBytes + Sized,
635+
{
636+
let byte_array: &ByteArray<Self> = TryFrom::try_from(&*bytes).ok()?;
637+
let aligned = Align::try_from_ref(byte_array)?;
638+
639+
if Self::is_bit_valid(aligned.into()) {
640+
Some(unsafe { &mut *bytes.as_mut_ptr().cast::<Self>() })
641+
} else {
642+
None
643+
}
644+
}
645+
646+
/// TODO
647+
fn try_read_from(bytes: &[u8]) -> Option<Self>
648+
where
649+
Self: Sized,
650+
// TODO: Why can't Rust infer this based on the blanket impl for `T:
651+
// Sized`? It sets `MaybeUninit = MaybeUninit<T>`, which is `Sized`.
652+
<Self as AsMaybeUninit>::MaybeUninit: Sized,
653+
{
654+
let byte_array: &ByteArray<Self> = TryFrom::try_from(bytes).ok()?;
655+
let maybe_valid = MaybeValid::from(byte_array.clone());
656+
657+
if Self::is_bit_valid(&maybe_valid) {
658+
Some(unsafe { maybe_valid.assume_valid() })
659+
} else {
660+
None
661+
}
662+
}
663+
}
664+
665+
unsafe impl<T: FromBytes> TryFromBytes for T {
666+
fn is_bit_valid(_candidate: &MaybeValid<Self>) -> bool
667+
where
668+
Self: Sized,
669+
{
670+
true
671+
}
672+
}
673+
674+
mod try_from_bytes_derive_example {
675+
use super::*;
676+
677+
struct Foo {
678+
a: u8,
679+
b: u16,
680+
}
681+
682+
impl_try_from_bytes!(Foo { a: u8, b: u16 });
683+
684+
struct Bar(Foo);
685+
686+
impl Bar {
687+
fn is_valid(&self) -> bool {
688+
u16::from(self.0.a) < self.0.b
689+
}
690+
}
691+
692+
impl_try_from_bytes!(Bar { 0: Foo } => is_valid);
693+
}
694+
505695
/// Types which are safe to treat as an immutable byte slice.
506696
///
507697
/// WARNING: Do not implement this trait yourself! Instead, use
@@ -1543,21 +1733,9 @@ impl<T: Display + ?Sized, A> Display for Align<T, A> {
15431733
}
15441734
}
15451735

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-
}
1736+
// unsafe impl<T: ?Sized, F: ?Sized, A> Projectable<F, Align<F, A>> for Align<T, A> {
1737+
// type Inner = T;
1738+
// }
15611739

15621740
/// A type with no alignment requirement.
15631741
///
@@ -2171,18 +2349,9 @@ impl<T> Ord for ByteArray<T> {
21712349
}
21722350
}
21732351

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-
}
2352+
// unsafe impl<T, F> Projectable<F, ByteArray<F>> for ByteArray<T> {
2353+
// type Inner = T;
2354+
// }
21862355

21872356
// Used in `transmute!` below.
21882357
#[doc(hidden)]

0 commit comments

Comments
 (0)