Skip to content

Commit f62b007

Browse files
committed
Add owned_slice to rustc_data_structures
The owned slice represents an owned value with a view into that owned slice. For example, it might own a `Vec<i32>` of `[1, 2]`, but only deref to `[2]`. It's almost entirely written using safe code, except for one unsafe block to calculate a pointer difference.
1 parent fee3a45 commit f62b007

File tree

2 files changed

+205
-0
lines changed

2 files changed

+205
-0
lines changed
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
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;
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
use std::ops::Deref;
2+
3+
use super::OwnedSlice;
4+
5+
#[test]
6+
fn create_deref() {
7+
let owned_slice = OwnedSlice::new(Box::new(vec![1, 2, 3]));
8+
9+
let slice = &*owned_slice;
10+
11+
assert_eq!(slice, &[1, 2, 3]);
12+
}
13+
14+
#[test]
15+
fn map() {
16+
let owned_slice: OwnedSlice<Box<dyn Deref<Target = [u8]>>, _> =
17+
OwnedSlice::new(Box::new(vec![1, 2, 3, 4, 5]));
18+
19+
let owned_slice = owned_slice.map(|slice| slice.split_at(2).1);
20+
let slice = &*owned_slice;
21+
22+
assert_eq!(slice, &[3, 4, 5]);
23+
}
24+
25+
#[test]
26+
fn empty_slice() {
27+
let owned_slice = OwnedSlice::new(Box::new(vec![1, 2, 3, 4, 5]));
28+
29+
let owned_slice = owned_slice.map(|slice| &slice[0..0]);
30+
31+
let slice = &*owned_slice;
32+
33+
assert_eq!(slice, &[]);
34+
}
35+
36+
#[test]
37+
#[should_panic]
38+
fn out_of_bounds() {
39+
static X: [u8; 5] = [1, 2, 3, 4, 5];
40+
41+
let owned_slice = OwnedSlice::new(Box::new(vec![1u8, 2, 3]));
42+
let owned_slice = owned_slice.map(|_| &X[..]);
43+
let slice = &*owned_slice;
44+
45+
assert_eq!(slice, &[1, 2, 3, 4, 5]);
46+
}
47+
48+
#[test]
49+
#[should_panic]
50+
fn no_zsts_allowed() {
51+
let other = Box::leak(Box::new(vec![(); 5]));
52+
ignore_leak(other);
53+
54+
let owned_slice = OwnedSlice::new(Box::new(vec![(); 5]));
55+
let owned_slice = owned_slice.map(|_| &other[..]);
56+
let slice = &*owned_slice;
57+
58+
assert_eq!(slice, other);
59+
}
60+
61+
/// It's ok for this to leak, we need a 'static reference.
62+
fn ignore_leak<T>(_ptr: *const T) {
63+
#[cfg(miri)]
64+
extern "Rust" {
65+
fn miri_static_root(ptr: *const u8);
66+
}
67+
#[cfg(miri)]
68+
unsafe {
69+
miri_static_root(_ptr.cast())
70+
};
71+
}

0 commit comments

Comments
 (0)