|
| 1 | +//! A type that stores a boxed type that derefs to a slice, and a custom index into that slice. |
| 2 | +//! |
| 3 | +//! The type can be mapped to have a different index, allowing you to take subslices while still |
| 4 | +//! preserving the original type. |
| 5 | +//! |
| 6 | +//! This is a specialized version of `owning_ref`, because `owning_ref` has various soundness |
| 7 | +//! issues that arise from its generality, that this one does not have. |
| 8 | +
|
| 9 | +// FIXME(nilstrieb): This could probably use some more trait implementations, |
| 10 | +// though they are currently not required in the compiler. |
| 11 | + |
| 12 | +use std::marker::PhantomData; |
| 13 | +use std::ops::Deref; |
| 14 | + |
| 15 | +/// An owned subslice of an owned value. |
| 16 | +/// The owned value must be behind a [`Deref`] indirection, for example a [`Box`] or an [`Lrc`]. |
| 17 | +/// |
| 18 | +/// The `OwnWrap` is an indirection that derefs to the inner owner (`Own`). The inner owner |
| 19 | +/// then derefs to the actual slice, of which this type references a subslice. |
| 20 | +/// |
| 21 | +/// Can be further subsliced using [`Self::map`]. |
| 22 | +/// |
| 23 | +/// [`Lrc`]: crate::sync::Lrc |
| 24 | +pub struct OwnedSlice<OwnWrap, T> { |
| 25 | + /// The wrapper around the owned value. Derefs to the owned value, which then derefs to the slice. |
| 26 | + owned: OwnWrap, |
| 27 | + /// The start value of the subslice. |
| 28 | + start: usize, |
| 29 | + /// The length of the subslice. |
| 30 | + len: usize, |
| 31 | + /// +--------------------------+ |
| 32 | + /// | We conceptually own a T. | |
| 33 | + /// +----+ +------------------+ |
| 34 | + /// \/ |
| 35 | + /// boo! ⊂(´・◡・⊂ )∘˚˳° |
| 36 | + _boo: PhantomData<T>, |
| 37 | +} |
| 38 | + |
| 39 | +impl<OwnWrap, Own: ?Sized, T> OwnedSlice<OwnWrap, T> |
| 40 | +where |
| 41 | + OwnWrap: Deref<Target = Own>, |
| 42 | + Own: Deref<Target = [T]> + 'static, |
| 43 | +{ |
| 44 | + /// Create a new `OwnedSlice<OwnWrap, T>`. Sets the subslice to contain the full slice that `OwnWrap` |
| 45 | + /// nestedly derefs to. |
| 46 | + pub fn new(owned: OwnWrap) -> Self { |
| 47 | + let len = owned.len(); |
| 48 | + Self { owned, start: 0, len, _boo: PhantomData } |
| 49 | + } |
| 50 | + |
| 51 | + /// Change the slice to a smaller subslice, while retaining ownership over the full value. |
| 52 | + /// |
| 53 | + /// # Panics |
| 54 | + /// Panics if the subslice is out of bounds of the smaller subslice. |
| 55 | + pub fn map<F>(self, f: F) -> Self |
| 56 | + where |
| 57 | + F: FnOnce(&[T]) -> &[T], |
| 58 | + { |
| 59 | + self.try_map::<_, !>(|slice| Ok(f(slice))).unwrap() |
| 60 | + } |
| 61 | + |
| 62 | + /// Map the slice to a subslice, while retaining ownership over the full value. |
| 63 | + /// The function may be fallible. |
| 64 | + /// |
| 65 | + /// # Panics |
| 66 | + /// Panics if the returned subslice is out of bounds of the base slice. |
| 67 | + pub fn try_map<F, E>(self, f: F) -> Result<Self, E> |
| 68 | + where |
| 69 | + F: FnOnce(&[T]) -> Result<&[T], E>, |
| 70 | + { |
| 71 | + let base_slice = self.base_slice(); |
| 72 | + let std::ops::Range { start: base_ptr, end: base_end_ptr } = base_slice.as_ptr_range(); |
| 73 | + let base_len = base_slice.len(); |
| 74 | + |
| 75 | + let slice = &base_slice[self.start..][..self.len]; |
| 76 | + let slice = f(slice)?; |
| 77 | + let (slice_ptr, len) = (slice.as_ptr(), slice.len()); |
| 78 | + |
| 79 | + let start = if len == 0 { |
| 80 | + // For empty slices, we don't really care where the start is. Also, the start of the subslice could |
| 81 | + // be a one-past-the-end pointer, which we cannot allow in the code below, but is ok here. |
| 82 | + 0 |
| 83 | + } else { |
| 84 | + // Assert that the start pointer is in bounds, I.E. points to the same allocated object. |
| 85 | + // If the slice is empty or contains a zero-sized type, the start and end pointers of the |
| 86 | + // base slice will always be the same, meaning this check will always fail. |
| 87 | + assert!(base_ptr <= slice_ptr); |
| 88 | + assert!(slice_ptr < base_end_ptr); |
| 89 | + |
| 90 | + // SAFETY: We have checked that the `slice_ptr` is bigger than the `base_ptr`. |
| 91 | + // We have also checked that it's in bounds of the allocated object. |
| 92 | + let diff_in_bytes = unsafe { slice_ptr.cast::<u8>().sub_ptr(base_ptr.cast::<u8>()) }; |
| 93 | + |
| 94 | + // The subslice might not actually be a difference of sizeof(T), but truncating it should be fine. |
| 95 | + diff_in_bytes / std::mem::size_of::<T>() |
| 96 | + }; |
| 97 | + |
| 98 | + // Assert that the length is not out of bounds. This is not nessecary for soundness, but helps detect errors |
| 99 | + // early, instead of panicking in the deref. |
| 100 | + assert!((start + len) <= base_len); |
| 101 | + |
| 102 | + Ok(Self { owned: self.owned, start, len, _boo: PhantomData }) |
| 103 | + } |
| 104 | + |
| 105 | + fn base_slice(&self) -> &[T] { |
| 106 | + &*self.owned |
| 107 | + } |
| 108 | +} |
| 109 | + |
| 110 | +impl<OwnWrap, Own: ?Sized, T> Deref for OwnedSlice<OwnWrap, T> |
| 111 | +where |
| 112 | + OwnWrap: Deref<Target = Own>, |
| 113 | + Own: Deref<Target = [T]> + 'static, |
| 114 | +{ |
| 115 | + type Target = [T]; |
| 116 | + |
| 117 | + fn deref(&self) -> &Self::Target { |
| 118 | + let base_slice = self.base_slice(); |
| 119 | + &base_slice[self.start..][..self.len] |
| 120 | + } |
| 121 | +} |
| 122 | + |
| 123 | +impl<OwnWrap, Own: ?Sized, T> std::borrow::Borrow<[T]> for OwnedSlice<OwnWrap, T> |
| 124 | +where |
| 125 | + OwnWrap: Deref<Target = Own>, |
| 126 | + Own: Deref<Target = [T]> + 'static, |
| 127 | +{ |
| 128 | + fn borrow(&self) -> &[T] { |
| 129 | + &*self |
| 130 | + } |
| 131 | +} |
| 132 | + |
| 133 | +#[cfg(test)] |
| 134 | +mod tests; |
0 commit comments