Skip to content

Commit ac0c27e

Browse files
authored
implement TryFromBytes for [T] (#666)
1 parent 122828e commit ac0c27e

File tree

2 files changed

+134
-12
lines changed

2 files changed

+134
-12
lines changed

src/lib.rs

+44-3
Original file line numberDiff line numberDiff line change
@@ -3511,8 +3511,8 @@ safety_comment! {
35113511
///
35123512
/// In other words, the layout of a `[T]` or `[T; N]` is a sequence of `T`s
35133513
/// laid out back-to-back with no bytes in between. Therefore, `[T]` or `[T;
3514-
/// N]` are `FromZeroes`, `FromBytes`, and `AsBytes` if `T` is
3515-
/// (respectively). Furthermore, since an array/slice has "the same
3514+
/// N]` are `TryFromBytes`, `FromZeroes`, `FromBytes`, and `AsBytes` if `T`
3515+
/// is (respectively). Furthermore, since an array/slice has "the same
35163516
/// alignment of `T`", `[T]` and `[T; N]` are `Unaligned` if `T` is.
35173517
///
35183518
/// Note that we don't `assert_unaligned!` for slice types because
@@ -3524,6 +3524,42 @@ safety_comment! {
35243524
unsafe_impl!(const N: usize, T: AsBytes => AsBytes for [T; N]);
35253525
unsafe_impl!(const N: usize, T: Unaligned => Unaligned for [T; N]);
35263526
assert_unaligned!([(); 0], [(); 1], [u8; 0], [u8; 1]);
3527+
unsafe_impl!(T: TryFromBytes => TryFromBytes for [T]; |c: Ptr<[T]>| {
3528+
// SAFETY: Assuming the preconditions of `is_bit_valid` are satisfied,
3529+
// so too will the postcondition: that, if `is_bit_valid(candidate)`
3530+
// returns true, `*candidate` contains a valid `Self`. Per the reference
3531+
// [1]:
3532+
//
3533+
// An array of `[T; N]` has a size of `size_of::<T>() * N` and the
3534+
// same alignment of `T`. Arrays are laid out so that the zero-based
3535+
// `nth` element of the array is offset from the start of the array by
3536+
// `n * size_of::<T>()` bytes.
3537+
//
3538+
// ...
3539+
//
3540+
// Slices have the same layout as the section of the array they slice.
3541+
//
3542+
// In other words, the layout of a `[T] is a sequence of `T`s laid out
3543+
// back-to-back with no bytes in between. If all elements in `candidate`
3544+
// are `is_bit_valid`, so too is `candidate`.
3545+
//
3546+
// Note that any of the below calls may panic, but it would still be
3547+
// sound even if it did. `is_bit_valid` does not promise that it will
3548+
// not panic (in fact, it explicitly warns that it's a possibility), and
3549+
// we have not violated any safety invariants that we must fix before
3550+
// returning.
3551+
c.iter().all(|elem|
3552+
// SAFETY: We uphold the safety contract of `is_bit_valid(elem)`, by
3553+
// precondition on the surrounding call to `is_bit_valid`. The
3554+
// memory referenced by `elem` is contained entirely within `c`, and
3555+
// satisfies the preconditions satisfied by `c`. By axiom, we assume
3556+
// that `Iterator:all` does not invalidate these preconditions
3557+
// (e.g., by writing to `elem`.) Since `elem` is derived from `c`,
3558+
// it is only possible for uninitialized bytes to occur in `elem` at
3559+
// the same bytes they occur within `c`.
3560+
unsafe { <T as TryFromBytes>::is_bit_valid(elem) }
3561+
)
3562+
});
35273563
unsafe_impl!(T: FromZeroes => FromZeroes for [T]);
35283564
unsafe_impl!(T: FromBytes => FromBytes for [T]);
35293565
unsafe_impl!(T: AsBytes => AsBytes for [T]);
@@ -7666,6 +7702,7 @@ mod tests {
76667702
@failure 0xD800u32, 0xDFFFu32, 0x110000u32;
76677703
str => @success "", "hello", "❤️🧡💛💚💙💜",
76687704
@failure [0, 159, 146, 150];
7705+
[u8] => @success [], [0, 1, 2];
76697706
NonZeroU8, NonZeroI8, NonZeroU16, NonZeroI16, NonZeroU32,
76707707
NonZeroI32, NonZeroU64, NonZeroI64, NonZeroU128, NonZeroI128,
76717708
NonZeroUsize, NonZeroIsize
@@ -7675,6 +7712,9 @@ mod tests {
76757712
// `0` may be any integer type with a different size or
76767713
// alignment than some `NonZeroXxx` types).
76777714
@failure Option::<Self>::None;
7715+
[bool]
7716+
=> @success [true, false], [false, true],
7717+
@failure [2u8], [3u8], [0xFFu8], [0u8, 1u8, 2u8];
76787718
);
76797719

76807720
// Asserts that `$ty` implements any `$trait` and doesn't implement any
@@ -7840,7 +7880,8 @@ mod tests {
78407880
assert_impls!(Unalign<u8>: KnownLayout, FromZeroes, FromBytes, AsBytes, Unaligned, !TryFromBytes);
78417881
assert_impls!(Unalign<NotZerocopy>: Unaligned, !KnownLayout, !TryFromBytes, !FromZeroes, !FromBytes, !AsBytes);
78427882

7843-
assert_impls!([u8]: KnownLayout, FromZeroes, FromBytes, AsBytes, Unaligned, !TryFromBytes);
7883+
assert_impls!([u8]: KnownLayout, TryFromBytes, FromZeroes, FromBytes, AsBytes, Unaligned);
7884+
assert_impls!([bool]: KnownLayout, TryFromBytes, FromZeroes, AsBytes, Unaligned, !FromBytes);
78447885
assert_impls!([NotZerocopy]: !KnownLayout, !TryFromBytes, !FromZeroes, !FromBytes, !AsBytes, !Unaligned);
78457886
assert_impls!([u8; 0]: KnownLayout, FromZeroes, FromBytes, AsBytes, Unaligned, !TryFromBytes);
78467887
assert_impls!([NotZerocopy; 0]: KnownLayout, !TryFromBytes, !FromZeroes, !FromBytes, !AsBytes, !Unaligned);

src/util.rs

+90-9
Original file line numberDiff line numberDiff line change
@@ -39,15 +39,15 @@ pub(crate) mod ptr {
3939
/// [covariant]: https://doc.rust-lang.org/reference/subtyping.html
4040
pub struct Ptr<'a, T: 'a + ?Sized> {
4141
// INVARIANTS:
42-
// - `ptr` is derived from some valid Rust allocation, `A`
43-
// - `ptr` has the same provenance as `A`
44-
// - `ptr` addresses a byte range which is entirely contained in `A`
45-
// - `ptr` addresses a byte range whose length fits in an `isize`
46-
// - `ptr` addresses a byte range which does not wrap around the address
47-
// space
48-
// - `ptr` is validly-aligned for `T`
49-
// - `A` is guaranteed to live for at least `'a`
50-
// - `T: 'a`
42+
// 1. `ptr` is derived from some valid Rust allocation, `A`
43+
// 2. `ptr` has the same provenance as `A`
44+
// 3. `ptr` addresses a byte range which is entirely contained in `A`
45+
// 4. `ptr` addresses a byte range whose length fits in an `isize`
46+
// 5. `ptr` addresses a byte range which does not wrap around the address
47+
// space
48+
// 6. `ptr` is validly-aligned for `T`
49+
// 7. `A` is guaranteed to live for at least `'a`
50+
// 8. `T: 'a`
5151
ptr: NonNull<T>,
5252
_lifetime: PhantomData<&'a ()>,
5353
}
@@ -255,6 +255,10 @@ pub(crate) mod ptr {
255255

256256
impl<'a, T> Ptr<'a, [T]> {
257257
/// The number of slice elements referenced by `self`.
258+
///
259+
/// # Safety
260+
///
261+
/// Unsafe code my rely on `len` satisfying the above contract.
258262
fn len(&self) -> usize {
259263
#[allow(clippy::as_conversions)]
260264
let slc = self.ptr.as_ptr() as *const [()];
@@ -286,6 +290,83 @@ pub(crate) mod ptr {
286290
// Nightly docs.
287291
slc.len()
288292
}
293+
294+
pub(crate) fn iter(&self) -> impl Iterator<Item = Ptr<'a, T>> {
295+
// TODO(#429): Once `NonNull::cast` documents that it preserves
296+
// provenance, cite those docs.
297+
let base = self.ptr.cast::<T>().as_ptr();
298+
(0..self.len()).map(move |i| {
299+
// TODO(https://github.com/rust-lang/rust/issues/74265): Use
300+
// `NonNull::get_unchecked_mut`.
301+
302+
// SAFETY: If the following conditions are not satisfied
303+
// `pointer::cast` may induce Undefined Behavior [1]:
304+
// > 1. Both the starting and resulting pointer must be either
305+
// > in bounds or one byte past the end of the same allocated
306+
// > object.
307+
// > 2. The computed offset, in bytes, cannot overflow an
308+
// > `isize`.
309+
// > 3. The offset being in bounds cannot rely on “wrapping
310+
// > around” the address space. That is, the
311+
// > infinite-precision sum must fit in a `usize`.
312+
//
313+
// [1] https://doc.rust-lang.org/std/primitive.pointer.html#method.add
314+
//
315+
// We satisfy all three of these conditions here:
316+
// 1. `base` (by invariant on `self`) points to an allocated
317+
// object. By contract, `self.len()` accurately reflects the
318+
// number of elements in the slice. `i` is in bounds of
319+
// `c.len()` by construction, and so the result of this
320+
// addition cannot overflow past the end of the allocation
321+
// referred to by `c`.
322+
// 2. By invariant on `Ptr`, `self` addresses a byte range whose
323+
// length fits in an `isize`. Since `elem` is contained in
324+
// `self`, the computed offset of `elem` must fit within
325+
// `isize.`
326+
// 3. By invariant on `Ptr`, `self` addresses a byte range which
327+
// does not wrap around the address space. Since `elem` is
328+
// contained in `self`, the computed offset of `elem` must
329+
// wrap around the address space.
330+
//
331+
// TODO(#429): Once `pointer::add` documents that it preserves
332+
// provenance, cite those docs.
333+
let elem = unsafe { base.add(i) };
334+
335+
// SAFETY:
336+
// - `elem` must not be null. `base` is constructed from a
337+
// `NonNull` pointer, and the addition that produces `elem`
338+
// must not overflow or wrap around, so `elem >= base > 0`.
339+
//
340+
// TODO(#429): Once `NonNull::new_unchecked` documents that it
341+
// preserves provenance, cite those docs.
342+
let elem = unsafe { NonNull::new_unchecked(elem) };
343+
344+
// SAFETY: The safety invariants of `Ptr` (see definition) are
345+
// satisfied:
346+
// 1. `elem` is derived from a valid Rust allocation, because
347+
// `self` is derived from a valid Rust allocation, by
348+
// invariant on `Ptr`
349+
// 2. `elem` has the same provenance as `self`, because it
350+
// derived from `self` using a series of
351+
// provenance-preserving operations
352+
// 3. `elem` is entirely contained in the allocation of `self`
353+
// (see above)
354+
// 4. `elem` addresses a byte range whose length fits in an
355+
// `isize` (see above)
356+
// 5. `elem` addresses a byte range which does not wrap around
357+
// the address space (see above)
358+
// 6. `elem` is validly-aligned for `T`. `self`, which
359+
// represents a `[T]` is validly aligned for `T`, and `elem`
360+
// is an element within that `[T]`
361+
// 7. The allocation of `elem` is guaranteed to live for at
362+
// least `'a`, because `elem` is entirely contained in
363+
// `self`, which lives for at least `'a` by invariant on
364+
// `Ptr`.
365+
// 8. `T: 'a`, because `elem` is an element within `[T]`, and
366+
// `[T]: 'a` by invariant on `Ptr`
367+
Ptr { ptr: elem, _lifetime: PhantomData }
368+
})
369+
}
289370
}
290371

291372
impl<'a, T: 'a + ?Sized> From<&'a T> for Ptr<'a, T> {

0 commit comments

Comments
 (0)