Skip to content

Commit

Permalink
Implement TryFromBytes for [T; N] (#799)
Browse files Browse the repository at this point in the history
Makes progress towards #5.
  • Loading branch information
jswrenn committed Jan 21, 2024
1 parent ce8cdcd commit a8a828c
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 3 deletions.
17 changes: 14 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3524,6 +3524,13 @@ safety_comment! {
///
/// [1] https://doc.rust-lang.org/reference/type-layout.html#array-layout
unsafe_impl!(const N: usize, T: NoCell => NoCell for [T; N]);
unsafe_impl!(const N: usize, T: TryFromBytes => TryFromBytes for [T; N]; |c: Maybe<[T; N]>| {
// Note that this call may panic, but it would still be sound even if it
// did. `is_bit_valid` does not promise that it will not panic (in fact,
// it explicitly warns that it's a possibility), and we have not
// violated any safety invariants that we must fix before returning.
<[T] as TryFromBytes>::is_bit_valid(c.as_slice())
});
unsafe_impl!(const N: usize, T: FromZeros => FromZeros for [T; N]);
unsafe_impl!(const N: usize, T: FromBytes => FromBytes for [T; N]);
unsafe_impl!(const N: usize, T: IntoBytes => IntoBytes for [T; N]);
Expand Down Expand Up @@ -7859,8 +7866,12 @@ mod tests {
// `0` may be any integer type with a different size or
// alignment than some `NonZeroXxx` types).
@failure Option::<Self>::None;
[bool; 0] => @success [];
[bool; 1]
=> @success [true], [false],
@failure [2u8], [3u8], [0xFFu8];
[bool]
=> @success [true, false], [false, true],
=> @success [true, false], [false, true],
@failure [2u8], [3u8], [0xFFu8], [0u8, 1u8, 2u8];
);

Expand Down Expand Up @@ -8276,11 +8287,11 @@ mod tests {
assert_impls!(
[u8; 0]: KnownLayout,
NoCell,
TryFromBytes,
FromZeros,
FromBytes,
IntoBytes,
Unaligned,
!TryFromBytes
);
assert_impls!(
[NotZerocopy; 0]: KnownLayout,
Expand All @@ -8294,11 +8305,11 @@ mod tests {
assert_impls!(
[u8; 1]: KnownLayout,
NoCell,
TryFromBytes,
FromZeros,
FromBytes,
IntoBytes,
Unaligned,
!TryFromBytes
);
assert_impls!(
[NotZerocopy; 1]: KnownLayout,
Expand Down
52 changes: 52 additions & 0 deletions src/pointer/ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -591,6 +591,58 @@ mod _casts {
}
}

impl<'a, const N: usize, T, I> Ptr<'a, [T; N], I>
where
T: 'a,
I: Invariants,
{
/// Casts this pointer-to-array into a slice.
#[allow(clippy::wrong_self_convention)]
pub(crate) fn as_slice(self) -> Ptr<'a, [T], I> {
let start = self.as_non_null().cast::<T>().as_ptr();
let slice = core::ptr::slice_from_raw_parts_mut(start, N);
// SAFETY: `slice` is not null, because it is derived from `start`
// which is non-null.
let slice = unsafe { NonNull::new_unchecked(slice) };
// SAFETY: Lemma: In the following safety arguments, note that
// `slice` is derived from `self` in two steps: first, by casting
// `self: [T; N]` to `start: T`, then by constructing a pointer to a
// slice starting at `start` of length `N`. As a result, `slice`
// references exactly the same allocation as `self.`
//
// 0. By the above lemma, `slice` is derived from the same
// allocation as `self`, which, by invariant on `Ptr`, is valid.
// 1. By the above lemma, `slice` has valid provenance for `A`,
// since it is derived from the pointer `self`, which, by
// invariant on `Ptr`, has valid provenance for `A`.
// 2. By the above lemma, `slice` addresses a byte range which is
// entirely contained in `A`, because it references exactly the
// same byte range as `self`, which, by invariant on `Ptr`, is
// entirely contained in `A`.
// 3. By the above lemma, `slice` addresses a byte range whose
// length fits in an `isize`, since it addresses exactly the same
// byte range as `self`, which, by invariant on `Ptr`, has a
// length that fits in an `isize`.
// 4. By the above lemma, `slice` addresses a byte range which does
// not wrap around the address space, since it addresses exactly
// the same byte range as `self`, which, by invariant on `Ptr`,
// does not wrap around the address space.
// 5. By the above lemma, `A` is guaranteed to live for at least
// `'a`, because it is derived from the same allocation as
// `self`, which, by invariant on `Ptr`, lives for at least `'a`.
// 6. By the above lemma, `slice` conforms to the aliasing invariant
// of `I::Aliasing`, because the operations that produced `slice`
// from `self` do not impact aliasing.
// 7. By the above lemma, `slice` conforms to the alignment
// invariant of `I::Alignment`, because the operations that
// produced `slice` from `self` do not impact alignment.
// 8. By the above lemma, `slice` conforms to the validity invariant
// of `I::Validity`, because the operations that produced `slice`
// from `self` do not impact validity.
unsafe { Ptr::new(slice) }
}
}

/// For caller convenience, these methods are generic over alignment
/// invariant. In practice, the referent is always well-aligned, because the
/// alignment of `[u8]` is 1.
Expand Down

0 comments on commit a8a828c

Please sign in to comment.