Skip to content

Commit f373439

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 f373439

File tree

2 files changed

+201
-27
lines changed

2 files changed

+201
-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

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

505+
/// TODO
506+
pub type AlignedByteArray<T> = Align<ByteArray<T>, T>;
507+
508+
/// A value which might or might not constitute a valid instance of `T`.
509+
///
510+
/// `MaybeValid<T>` has the same layout (size and alignment) and field offsets
511+
/// as `T`. However, it may contain any bit pattern with a few restrictions:
512+
/// Given `m: MaybeValid<T>` and a byte offset, `b` in the range `[0,
513+
/// size_of::<MaybeValid<T>>())`:
514+
/// - If, in all valid instances `t: T`, the byte at offset `b` in `t` is
515+
/// initialized, then the byte at offset `b` within `m` is guaranteed to be
516+
/// initialized.
517+
/// - Let `s` be the sequence of bytes of length `b` in the offset range `[0,
518+
/// b)` in `m`. Let `TT` be the subset of valid instances of `T` which contain
519+
/// this sequence in the offset range `[0, b)`. If, for all instances of `t:
520+
/// T` in `TT`, the byte at offset `b` in `t` is initialized, then the byte at
521+
/// offset `b` in `m` is guaranteed to be initialized.
522+
///
523+
/// Pragmatically, this means that if `m` is guaranteed to contain an enum
524+
/// type at a particular offset, and the enum discriminant stored in `m`
525+
/// corresponds to a valid variant of that enum type, then it is guaranteed
526+
/// that the appropriate bytes of `m` are initialized as defined by that
527+
/// variant's layout (although note that the variant's layout may contain
528+
/// another enum type, in which case the same rules apply depending on the
529+
/// state of its discriminant, and so on recursively).
530+
///
531+
/// # Safety
532+
///
533+
/// TODO (make sure to mention enum layout)
534+
#[repr(transparent)]
535+
pub struct MaybeValid<T> {
536+
inner: MaybeUninit<T>,
537+
}
538+
539+
impl<T> MaybeValid<T> {
540+
/// TODO
541+
pub const unsafe fn assume_valid(self) -> T {
542+
unsafe { self.inner.assume_init() }
543+
}
544+
}
545+
546+
unsafe impl<T, F> Projectable<F, MaybeValid<F>> for MaybeValid<T> {
547+
type Inner = T;
548+
}
549+
550+
impl<T> From<ByteArray<T>> for MaybeValid<T> {
551+
fn from(bytes: ByteArray<T>) -> MaybeValid<T> {
552+
todo!()
553+
}
554+
}
555+
556+
impl<'a, T> From<&'a Align<ByteArray<T>, T>> for &'a MaybeValid<T> {
557+
fn from(bytes: &'a Align<ByteArray<T>, T>) -> &'a MaybeValid<T> {
558+
todo!()
559+
}
560+
}
561+
562+
impl<'a, T> From<&'a mut Align<ByteArray<T>, T>> for &'a mut MaybeValid<T> {
563+
fn from(bytes: &'a mut Align<ByteArray<T>, T>) -> &'a mut MaybeValid<T> {
564+
todo!()
565+
}
566+
}
567+
568+
impl<T> Debug for MaybeValid<T> {
569+
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
570+
f.pad(core::any::type_name::<Self>())
571+
}
572+
}
573+
574+
/// TODO
575+
pub unsafe trait TryFromBytes {
576+
/// TODO
577+
fn is_bit_valid(candidate: &MaybeValid<Self>) -> bool
578+
where
579+
Self: Sized;
580+
581+
/// TODO
582+
// Note that, in a future in which we distinguish between `FromBytes` and `RefFromBytes`,
583+
// this requires `where Self: RefFromBytes` to disallow interior mutability.
584+
fn try_from_ref(bytes: &[u8]) -> Option<&Self>
585+
where
586+
Self: Sized,
587+
{
588+
let byte_array: &ByteArray<Self> = TryFrom::try_from(bytes).ok()?;
589+
let aligned = Align::try_from_ref(byte_array)?;
590+
591+
if Self::is_bit_valid(aligned.into()) {
592+
Some(unsafe { &*bytes.as_ptr().cast::<Self>() })
593+
} else {
594+
None
595+
}
596+
}
597+
598+
/// TODO
599+
fn try_from_mut(bytes: &mut [u8]) -> Option<&mut Self>
600+
where
601+
Self: AsBytes + Sized,
602+
{
603+
let byte_array: &ByteArray<Self> = TryFrom::try_from(&*bytes).ok()?;
604+
let aligned = Align::try_from_ref(byte_array)?;
605+
606+
if Self::is_bit_valid(aligned.into()) {
607+
Some(unsafe { &mut *bytes.as_mut_ptr().cast::<Self>() })
608+
} else {
609+
None
610+
}
611+
}
612+
613+
/// TODO
614+
fn try_read_from(bytes: &[u8]) -> Option<Self>
615+
where
616+
Self: Sized,
617+
{
618+
let byte_array: &ByteArray<Self> = TryFrom::try_from(bytes).ok()?;
619+
let maybe_valid = MaybeValid::from(byte_array.clone());
620+
621+
if Self::is_bit_valid(&maybe_valid) {
622+
Some(unsafe { maybe_valid.assume_valid() })
623+
} else {
624+
None
625+
}
626+
}
627+
}
628+
629+
unsafe impl<T: FromBytes> TryFromBytes for T {
630+
fn is_bit_valid(_candidate: &MaybeValid<Self>) -> bool
631+
where
632+
Self: Sized,
633+
{
634+
true
635+
}
636+
}
637+
638+
mod try_from_bytes_derive_example {
639+
use super::*;
640+
641+
struct Foo {
642+
a: u8,
643+
b: u16,
644+
}
645+
646+
impl_try_from_bytes!(Foo { a: u8, b: u16 });
647+
648+
struct Bar(Foo);
649+
650+
impl Bar {
651+
fn is_valid(&self) -> bool {
652+
u16::from(self.0.a) < self.0.b
653+
}
654+
}
655+
656+
impl_try_from_bytes!(Bar { 0: Foo } => is_valid);
657+
}
658+
505659
/// Types which are safe to treat as an immutable byte slice.
506660
///
507661
/// WARNING: Do not implement this trait yourself! Instead, use
@@ -1543,21 +1697,9 @@ impl<T: Display + ?Sized, A> Display for Align<T, A> {
15431697
}
15441698
}
15451699

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-
}
1700+
// unsafe impl<T: ?Sized, F: ?Sized, A> Projectable<F, Align<F, A>> for Align<T, A> {
1701+
// type Inner = T;
1702+
// }
15611703

15621704
/// A type with no alignment requirement.
15631705
///
@@ -2171,18 +2313,9 @@ impl<T> Ord for ByteArray<T> {
21712313
}
21722314
}
21732315

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-
}
2316+
// unsafe impl<T, F> Projectable<F, ByteArray<F>> for ByteArray<T> {
2317+
// type Inner = T;
2318+
// }
21862319

21872320
// Used in `transmute!` below.
21882321
#[doc(hidden)]
@@ -3668,6 +3801,21 @@ mod alloc_support {
36683801
#[doc(inline)]
36693802
pub use alloc_support::*;
36703803

3804+
/// Transmutes a `T` into a `U`.
3805+
///
3806+
/// # Safety
3807+
///
3808+
/// Safety requirements are the same as for `core::mem::transmute`, except that
3809+
/// the caller must also ensure that `size_of::<T>() == size_of::<U>()`.
3810+
unsafe fn transmute_size_unchecked<T, U>(t: T) -> U {
3811+
union Transmute<T, U> {
3812+
t: ManuallyDrop<T>,
3813+
u: ManuallyDrop<U>,
3814+
}
3815+
3816+
unsafe { ManuallyDrop::into_inner(Transmute { t: ManuallyDrop::new(t) }.u) }
3817+
}
3818+
36713819
#[cfg(test)]
36723820
mod tests {
36733821
#![allow(clippy::unreadable_literal)]

0 commit comments

Comments
 (0)