Skip to content

Commit c83d465

Browse files
committed
Generalize split_at_unchecked
Makes progress towards #1290.
1 parent 04cafb2 commit c83d465

File tree

4 files changed

+210
-53
lines changed

4 files changed

+210
-53
lines changed

src/lib.rs

+43
Original file line numberDiff line numberDiff line change
@@ -749,6 +749,11 @@ pub unsafe trait KnownLayout {
749749
#[doc(hidden)]
750750
type MaybeUninit: ?Sized + KnownLayout<PointerMetadata = Self::PointerMetadata>;
751751

752+
/// The type of the trailing element slice.
753+
///
754+
/// If `Self` is a DST, then `TrailingElem` is `0`.
755+
type TrailingElem: Sized;
756+
752757
/// The layout of `Self`.
753758
///
754759
/// # Safety
@@ -830,6 +835,22 @@ pub trait PointerMetadata: Copy + Eq + Debug {
830835
/// `size_for_metadata` promises to only return `None` if the resulting size
831836
/// would not fit in a `usize`.
832837
fn size_for_metadata(&self, layout: DstLayout) -> Option<usize>;
838+
839+
/// Computes the trailing padding bytes object with the given layout and
840+
/// pointer metadata.
841+
///
842+
/// # Panics
843+
///
844+
/// If `Self = ()`, `layout` must describe a sized type. If `Self = usize`,
845+
/// `layout` must describe a slice DST. Otherwise, `padding_needed_for` may
846+
/// panic.
847+
///
848+
/// # Safety
849+
///
850+
/// `padding_needed_for` promises to only return `None` if the resulting
851+
/// padding would not fit in a `usize`. (TODO: Can we prove this is
852+
/// impossible?)
853+
fn padding_needed_for(&self, layout: DstLayout) -> Option<usize>;
833854
}
834855

835856
impl PointerMetadata for () {
@@ -846,6 +867,14 @@ impl PointerMetadata for () {
846867
SizeInfo::SliceDst(_) => None,
847868
}
848869
}
870+
871+
#[inline]
872+
fn padding_needed_for(&self, layout: DstLayout) -> Option<usize> {
873+
match layout.size_info {
874+
SizeInfo::Sized { .. } => Some(0),
875+
SizeInfo::SliceDst(..) => None,
876+
}
877+
}
849878
}
850879

851880
impl PointerMetadata for usize {
@@ -867,6 +896,18 @@ impl PointerMetadata for usize {
867896
SizeInfo::Sized { .. } => None,
868897
}
869898
}
899+
900+
#[inline]
901+
fn padding_needed_for(&self, layout: DstLayout) -> Option<usize> {
902+
match layout.size_info {
903+
SizeInfo::Sized { .. } => Some(0),
904+
SizeInfo::SliceDst(TrailingSliceLayout { offset, elem_size }) => {
905+
let trailing_size = self.checked_mul(elem_size)?;
906+
let unpadded_size = trailing_size.checked_add(offset)?;
907+
Some(util::padding_needed_for(unpadded_size, layout.align))
908+
}
909+
}
910+
}
870911
}
871912

872913
// SAFETY: Delegates safety to `DstLayout::for_slice`.
@@ -913,6 +954,8 @@ unsafe impl<T> KnownLayout for [T] {
913954
// size_of::<T>()` bytes.
914955
type MaybeUninit = [CoreMaybeUninit<T>];
915956

957+
type TrailingElem = T;
958+
916959
const LAYOUT: DstLayout = DstLayout::for_slice::<T>();
917960

918961
// SAFETY: `.cast` preserves address and provenance. The returned pointer

src/pointer/inner.rs

+159-53
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,159 @@ impl<'a, T: ?Sized> PtrInner<'a, T> {
139139
}
140140
}
141141

142+
#[allow(clippy::needless_lifetimes)]
143+
impl<'a, T> PtrInner<'a, T>
144+
where
145+
T: ?Sized + KnownLayout,
146+
{
147+
/// Produces the metadata of this `ptr`.
148+
///
149+
/// # Safety
150+
///
151+
/// Unsafe code my rely on `len` satisfying the above contract.
152+
pub(crate) fn meta(self) -> T::PointerMetadata {
153+
T::pointer_to_metadata(self.as_non_null().as_ptr())
154+
}
155+
156+
/// Produces a `PtrInner` with the same address and provenance as `self` but
157+
/// the given `meta`.
158+
///
159+
/// # Safety
160+
///
161+
/// The caller promises that if `self`'s referent is not zero sized, then
162+
/// a pointer constructed from its address with the given `meta` metadata
163+
/// will address a subset of the allocation pointed to by `self`.
164+
#[inline]
165+
pub(crate) unsafe fn with_meta(self, meta: T::PointerMetadata) -> Self
166+
where
167+
T: KnownLayout,
168+
{
169+
let raw = T::raw_from_ptr_len(self.as_non_null().cast(), meta);
170+
171+
// SAFETY:
172+
//
173+
// Lemma 0: `raw` either addresses zero bytes, or addresses a subset of
174+
// the allocation pointed to by `self` and has the same
175+
// provenance as `self`. Proof: `raw` is constructed using
176+
// provenance-preserving operations, and the caller has
177+
// promised that, if `self`'s referent is not zero-sized, the
178+
// resulting pointer addresses a subset of the allocation
179+
// pointed to by `self`.
180+
//
181+
// 0. Per Lemma 0 and by invariant on `self`, if `ptr`'s referent is not
182+
// zero sized, then `ptr` is derived from some valid Rust allocation,
183+
// `A`.
184+
// 1. Per Lemma 0 and by invariant on `self`, if `ptr`'s referent is not
185+
// zero sized, then `ptr` has valid provenance for `A`.
186+
// 2. Per Lemma 0 and by invariant on `self`, if `ptr`'s referent is not
187+
// zero sized, then `ptr` addresses a byte range which is entirely
188+
// contained in `A`.
189+
// 3. Per Lemma 0 and by invariant on `self`, `ptr` addresses a byte
190+
// range whose length fits in an `isize`.
191+
// 4. Per Lemma 0 and by invariant on `self`, `ptr` addresses a byte
192+
// range which does not wrap around the address space.
193+
// 5. Per Lemma 0 and by invariant on `self`, if `ptr`'s referent is not
194+
// zero sized, then `A` is guaranteed to live for at least `'a`.
195+
unsafe { PtrInner::new(raw) }
196+
}
197+
}
198+
199+
#[allow(clippy::needless_lifetimes)]
200+
impl<'a, T> PtrInner<'a, T>
201+
where
202+
T: ?Sized + KnownLayout<PointerMetadata = usize>,
203+
{
204+
/// The number of trailing slice elements in the object referenced by
205+
/// `self`.
206+
///
207+
/// # Safety
208+
///
209+
/// Unsafe code my rely on `len` satisfying the above contract.
210+
pub(crate) fn len(&self) -> usize {
211+
self.meta()
212+
}
213+
214+
/// Splits `T` into two.
215+
///
216+
/// # Safety
217+
///
218+
/// The caller promises that:
219+
/// - `l_len <= self.len()`.
220+
///
221+
/// Given `let (left, right) = ptr.split_at(l_len)`, it is guaranteed
222+
/// that `left` and `right` are contiguous and non-overlapping if
223+
/// `self.padding_needed_for_len(l_len) == Some(0)`. This is true for
224+
/// all `[T]`.
225+
///
226+
/// If `self.padding_needed_for_len(l_len) != Some(0)`, then the left
227+
/// pointer will overlap the right pointer to satisfy `T`'s padding
228+
/// requirements.
229+
pub(crate) unsafe fn split_at_unchecked(
230+
self,
231+
l_len: usize,
232+
) -> (Self, PtrInner<'a, [T::TrailingElem]>) {
233+
// SAFETY: The caller promises that `l_len <= self.len()`.
234+
// Trivially, `0 <= l_len`.
235+
let left = unsafe { self.with_meta(l_len) };
236+
237+
let right = self.trailing_slice();
238+
// SAFETY: The caller promises that `l_len <= self.len() =
239+
// slf.len()`. Trivially, `slf.len() <= slf.len()`.
240+
let right = unsafe { right.slice_unchecked(l_len..self.len()) };
241+
242+
// SAFETY: If `self.padding_needed_for_len(l_len) == Some(0)`, then
243+
// `left` and `right` are non-overlapping. Proof: `left` is constructed
244+
// `slf` with `l_len` as its (exclusive) upper bound. If
245+
// `self.padding_needed_for_len(l_len) == Some(0)`, then `left` requires
246+
// no trailing padding following its final element. Since `right` is
247+
// constructed from `slf`'s trailing slice with `l_len` as its
248+
// (inclusive) lower bound, no byte is referred to by both pointers.
249+
//
250+
// Conversely, `self.padding_needed_for_len(l_len) == Some(N)`, where `N
251+
// > 0`, `left` requires `N` bytes of trailing padding following its
252+
// final element. Since `right` is constructed from the trailing slice
253+
// of `slf` with `l_len` as its (inclusive) lower bound, the first `N`
254+
// bytes of `right` are aliased by `left`.
255+
(left, right)
256+
}
257+
258+
/// Produces the number of padding bytes that must follow the trailing
259+
/// element of `self` if its trailing slice has length `len`.
260+
///
261+
/// # Safety
262+
///
263+
/// Unsafe code my rely on `len` satisfying the above contract.
264+
#[allow(unused)]
265+
pub(crate) fn padding_needed_for_len(&self, len: usize) -> Option<usize> {
266+
len.padding_needed_for(T::LAYOUT)
267+
}
268+
269+
/// Produces the trailing slice of `self`.
270+
///
271+
/// # Safety
272+
///
273+
/// Unsafe code my rely on `len` satisfying the above contract.
274+
pub(crate) fn trailing_slice(self) -> PtrInner<'a, [T::TrailingElem]> {
275+
let offset = match T::LAYOUT.size_info {
276+
crate::SizeInfo::Sized { size } => size,
277+
crate::SizeInfo::SliceDst(crate::TrailingSliceLayout { offset, .. }) => offset,
278+
};
279+
280+
let bytes = self.as_non_null().cast::<u8>().as_ptr();
281+
282+
// SAFETY: TODO.
283+
let bytes = unsafe { bytes.add(offset) };
284+
285+
// SAFETY: TODO
286+
let bytes = unsafe { NonNull::new_unchecked(bytes) };
287+
288+
let raw = KnownLayout::raw_from_ptr_len(bytes, self.len());
289+
290+
// SAFETY: TODO
291+
unsafe { PtrInner::new(raw) }
292+
}
293+
}
294+
142295
#[allow(clippy::needless_lifetimes)]
143296
impl<'a, T> PtrInner<'a, [T]> {
144297
/// Creates a pointer which addresses the given `range` of self.
@@ -200,31 +353,6 @@ impl<'a, T> PtrInner<'a, [T]> {
200353
unsafe { PtrInner::new(ptr) }
201354
}
202355

203-
/// Splits the slice in two.
204-
///
205-
/// # Safety
206-
///
207-
/// The caller promises that `l_len <= self.len()`.
208-
///
209-
/// Given `let (left, right) = ptr.split_at(l_len)`, it is guaranteed
210-
/// that `left` and `right` are contiguous and non-overlapping.
211-
pub(crate) unsafe fn split_at(self, l_len: usize) -> (Self, Self) {
212-
// SAFETY: The caller promises that `l_len <= self.len()`.
213-
// Trivially, `0 <= l_len`.
214-
let left = unsafe { self.slice_unchecked(0..l_len) };
215-
216-
// SAFETY: The caller promises that `l_len <= self.len() =
217-
// slf.len()`. Trivially, `slf.len() <= slf.len()`.
218-
let right = unsafe { self.slice_unchecked(l_len..self.len()) };
219-
220-
// SAFETY: `left` and `right` are non-overlapping. Proof: `left` is
221-
// constructed from `slf` with `l_len` as its (exclusive) upper
222-
// bound, while `right` is constructed from `slf` with `l_len` as
223-
// its (inclusive) lower bound. Thus, no index is a member of both
224-
// ranges.
225-
(left, right)
226-
}
227-
228356
/// Iteratively projects the elements `PtrInner<T>` from `PtrInner<[T]>`.
229357
pub(crate) fn iter(&self) -> impl Iterator<Item = PtrInner<'a, T>> {
230358
// TODO(#429): Once `NonNull::cast` documents that it preserves
@@ -297,32 +425,6 @@ impl<'a, T> PtrInner<'a, [T]> {
297425
unsafe { PtrInner::new(elem) }
298426
})
299427
}
300-
301-
/// The number of slice elements in the object referenced by `self`.
302-
///
303-
/// # Safety
304-
///
305-
/// Unsafe code my rely on `len` satisfying the above contract.
306-
pub(crate) fn len(&self) -> usize {
307-
self.trailing_slice_len()
308-
}
309-
}
310-
311-
#[allow(clippy::needless_lifetimes)]
312-
impl<'a, T> PtrInner<'a, T>
313-
where
314-
T: ?Sized + KnownLayout<PointerMetadata = usize>,
315-
{
316-
/// The number of trailing slice elements in the object referenced by
317-
/// `self`.
318-
///
319-
/// # Safety
320-
///
321-
/// Unsafe code my rely on `trailing_slice_len` satisfying the above
322-
/// contract.
323-
pub(super) fn trailing_slice_len(&self) -> usize {
324-
T::pointer_to_metadata(self.as_non_null().as_ptr())
325-
}
326428
}
327429

328430
impl<'a, T, const N: usize> PtrInner<'a, [T; N]> {
@@ -442,7 +544,11 @@ impl<'a> PtrInner<'a, [u8]> {
442544

443545
// SAFETY: `validate_cast_and_convert_metadata` promises to return
444546
// `split_at <= self.len()`.
445-
let (l_slice, r_slice) = unsafe { self.split_at(split_at) };
547+
//
548+
// Lemma 0: `l_slice` and `r_slice` are non-overlapping. Proof: By
549+
// contract on `PtrInner::split_at_unchecked`, the produced `PtrInner`s
550+
// are always non-overlapping if `self` is a `[T]`; here it is a `[u8]`.
551+
let (l_slice, r_slice) = unsafe { self.split_at_unchecked(split_at) };
446552

447553
let (target, remainder) = match cast_type {
448554
CastType::Prefix => (l_slice, r_slice),
@@ -500,7 +606,7 @@ mod tests {
500606
for i in 0..=N {
501607
assert_eq!(ptr.len(), N);
502608
// SAFETY: `i` is in bounds by construction.
503-
let (l, r) = unsafe { ptr.split_at(i) };
609+
let (l, r) = unsafe { ptr.split_at_unchecked(i) };
504610
// SAFETY: Points to a valid value by construction.
505611
#[allow(clippy::undocumented_unsafe_blocks)] // Clippy false positive
506612
let l_sum: usize = l

src/util/macros.rs

+3
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,8 @@ macro_rules! impl_known_layout {
454454
// alignment, and ABI as `T`
455455
type MaybeUninit = core::mem::MaybeUninit<Self>;
456456

457+
type TrailingElem = ();
458+
457459
const LAYOUT: crate::DstLayout = crate::DstLayout::for_type::<$ty>();
458460

459461
// SAFETY: `.cast` preserves address and provenance.
@@ -497,6 +499,7 @@ macro_rules! unsafe_impl_known_layout {
497499

498500
type PointerMetadata = <$repr as KnownLayout>::PointerMetadata;
499501
type MaybeUninit = <$repr as KnownLayout>::MaybeUninit;
502+
type TrailingElem = <$repr as KnownLayout>::TrailingElem;
500503

501504
const LAYOUT: DstLayout = <$repr as KnownLayout>::LAYOUT;
502505

zerocopy-derive/src/lib.rs

+5
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,8 @@ fn derive_known_layout_inner(ast: &DeriveInput, _top_level: Trait) -> Result<Tok
225225

226226
type MaybeUninit = __ZerocopyKnownLayoutMaybeUninit #ty_generics;
227227

228+
type TrailingElem = <#trailing_field_ty as ::zerocopy::KnownLayout>::TrailingElem;
229+
228230
// SAFETY: `LAYOUT` accurately describes the layout of `Self`.
229231
// The layout of `Self` is reflected using a sequence of
230232
// invocations of `DstLayout::{new_zst,extend,pad_to_align}`.
@@ -364,6 +366,8 @@ fn derive_known_layout_inner(ast: &DeriveInput, _top_level: Trait) -> Result<Tok
364366

365367
type MaybeUninit = Self;
366368

369+
type TrailingElem = <#trailing_field_ty as ::zerocopy::KnownLayout>::TrailingElem;
370+
367371
const LAYOUT: ::zerocopy::DstLayout = <#ident #ty_generics as ::zerocopy::KnownLayout>::LAYOUT;
368372

369373
#methods
@@ -382,6 +386,7 @@ fn derive_known_layout_inner(ast: &DeriveInput, _top_level: Trait) -> Result<Tok
382386
type PointerMetadata = ();
383387
type MaybeUninit =
384388
::zerocopy::util::macro_util::core_reexport::mem::MaybeUninit<Self>;
389+
type TrailingElem = ();
385390

386391
// SAFETY: `LAYOUT` is guaranteed to accurately describe the
387392
// layout of `Self`, because that is the documented safety

0 commit comments

Comments
 (0)