indexing/
container.rs

1#[cfg(feature = "doc")]
2use crate::{scope, scope_ref};
3use {
4    crate::{
5        index::{Index, IndexError, Range},
6        proof::{Id, NonEmpty, Unknown},
7        traits::{Idx, TrustedContainer, TrustedItem},
8    },
9    core::{fmt, ops},
10};
11
12/// A branded container, that allows access only to indices and ranges with
13/// the exact same brand in the `'id` parameter.
14///
15/// The elements in the underlying data structure are accessible partly
16/// through special purpose methods, and through indexing/slicing.
17///
18/// The container can be indexed with `self[i]` where `i` is a trusted,
19/// dereferenceable index or range. Indexing like this uses _no_ runtime
20/// checking at all, as it is statically guaranteed correct.
21#[repr(transparent)]
22pub struct Container<'id, Array: TrustedContainer + ?Sized> {
23    #[allow(unused)]
24    id: Id<'id>,
25    array: Array,
26}
27
28impl<'id, Array: TrustedContainer> Container<'id, Array> {
29    pub(crate) unsafe fn new(array: Array) -> Self {
30        Container {
31            id: Id::default(),
32            array,
33        }
34    }
35}
36
37impl<'id, Array: TrustedContainer + ?Sized> Container<'id, &Array> {
38    pub fn project(&self) -> &Container<'id, Array> {
39        unsafe { &*(self.array as *const Array as *const Container<'id, Array>) }
40    }
41}
42
43impl<'id, Array: TrustedContainer + ?Sized> Container<'id, Array> {
44    /// This container without the branding.
45    ///
46    /// # Note
47    ///
48    /// The returned lifetime of `&Array` is _not_ `'id`! It's completely
49    /// valid to drop the container during the [`scope`], in which case this
50    /// reference would become invalid. If you need a longer lifetime,
51    /// consider using [`scope_ref`] such that the reference is guaranteed to
52    /// live for the entire scope.
53    pub fn untrusted(&self) -> &Array {
54        &self.array
55    }
56
57    /// The length of the container in base item units.
58    pub fn unit_len(&self) -> usize {
59        self.array.unit_len()
60    }
61
62    /// The zero index without a proof of contents.
63    pub fn start<I: Idx>(&self) -> Index<'id, I, Unknown> {
64        unsafe { Index::new(I::zero()) }
65    }
66
67    /// The index one past the end of this container.
68    pub fn end<I: Idx>(&self) -> Index<'id, I, Unknown> {
69        let len = I::from_usize(self.unit_len()).expect("len");
70        unsafe { Index::new(len) }
71    }
72
73    /// The empty range `0..0`.
74    pub fn empty_range<I: Idx>(&self) -> Range<'id, I, Unknown> {
75        Range::from(self.start(), self.start())
76    }
77
78    /// The full range of the container.
79    pub fn range<I: Idx>(&self) -> Range<'id, I, Unknown> {
80        Range::from(self.start(), self.end())
81    }
82
83    /// Vet an absolute index.
84    pub fn vet<I: Idx>(&self, idx: I) -> Result<Index<'id, I, NonEmpty>, IndexError> {
85        // TrustedItem::vet doesn't assert that it's not at the end
86        let item = TrustedItem::vet(idx, self)?;
87        if item < self.end() {
88            unsafe { Ok(Index::new_nonempty(item.untrusted())) }
89        } else {
90            Err(IndexError::OutOfBounds)
91        }
92    }
93
94    /// Vet an absolute range.
95    // Future: Error type `EitherOrBoth<IndexError, IndexError>`?
96    pub fn vet_range<I: Idx>(
97        &self,
98        r: ops::Range<I>,
99    ) -> Result<Range<'id, I, Unknown>, IndexError> {
100        Ok(Range::from(
101            TrustedItem::vet(r.start, self)?,
102            TrustedItem::vet(r.end, self)?,
103        ))
104    }
105
106    /// Split the container in two at the given index,
107    /// such that the second range contains the index.
108    pub fn split_at<I: Idx, P>(
109        &self,
110        idx: Index<'id, I, P>,
111    ) -> (Range<'id, I, Unknown>, Range<'id, I, P>) {
112        (Range::from(self.start(), idx), unsafe {
113            Range::new_any(idx.untrusted(), self.end().untrusted())
114        })
115    }
116
117    /// Split the container in two after the given index,
118    /// such that the first range contains the index.
119    pub fn split_after<I: Idx>(
120        &self,
121        idx: Index<'id, I, NonEmpty>,
122    ) -> (Range<'id, I, NonEmpty>, Range<'id, I, Unknown>) {
123        let mid = TrustedItem::after(idx, self);
124        (
125            unsafe { Range::new_nonempty(I::zero(), mid.untrusted()) },
126            Range::from(mid, self.end()),
127        )
128    }
129
130    /// Split around the range `r` creating ranges `0..r.start` and `r.end..`.
131    ///
132    /// The input `r` and return values `(s, t)` cover teh whole container in
133    /// the order `s`, `r`, `t`.
134    pub fn split_around<I: Idx, P>(
135        &self,
136        r: Range<'id, I, P>,
137    ) -> (Range<'id, I, Unknown>, Range<'id, I, Unknown>) {
138        (
139            Range::from(self.start(), r.start()),
140            Range::from(r.end(), self.end()),
141        )
142    }
143
144    /// Return the range before but not including the index.
145    pub fn before<I: Idx, P>(&self, idx: Index<'id, I, P>) -> Range<'id, I, Unknown> {
146        Range::from(self.start(), idx)
147    }
148
149    /// Return the range after but not including the index.
150    pub fn after<I: Idx>(&self, idx: Index<'id, I, NonEmpty>) -> Range<'id, I, Unknown> {
151        let after = TrustedItem::after(idx, self);
152        Range::from(after, self.end())
153    }
154
155    /// Advance an index to the next item in the container, if there is one.
156    pub fn advance<I: Idx>(&self, idx: Index<'id, I, NonEmpty>) -> Option<Index<'id, I, NonEmpty>> {
157        TrustedItem::advance(idx, self)
158    }
159
160    /// Advance an index by a given base unit offset,
161    /// if the index at said offset is a valid item index.
162    pub fn advance_by<I: Idx, P>(
163        &self,
164        idx: Index<'id, I, P>,
165        offset: usize,
166    ) -> Result<Index<'id, I, NonEmpty>, IndexError> {
167        self.vet(idx.untrusted().add(offset))
168    }
169
170    /// Decrease an index by a given base unit offset,
171    /// if the index at said offset is a valid item index.
172    pub fn decrease_by<I: Idx, P>(
173        &self,
174        idx: Index<'id, I, P>,
175        offset: usize,
176    ) -> Result<Index<'id, I, NonEmpty>, IndexError> {
177        if idx.untrusted().as_usize() >= offset {
178            self.vet(idx.untrusted().sub(offset))
179        } else {
180            Err(IndexError::OutOfBounds)
181        }
182    }
183}
184
185impl<'id, Array: TrustedContainer + ?Sized, I: Idx> ops::Index<Index<'id, I, NonEmpty>>
186    for Container<'id, Array>
187{
188    type Output = Array::Item;
189
190    fn index(&self, index: Index<'id, I, NonEmpty>) -> &Self::Output {
191        unsafe { self.array.get_unchecked(index.untrusted().as_usize()) }
192    }
193}
194
195impl<'id, Array: TrustedContainer + ?Sized, I: Idx, P> ops::Index<Range<'id, I, P>>
196    for Container<'id, Array>
197{
198    type Output = Array::Slice;
199
200    fn index(&self, r: Range<'id, I, P>) -> &Self::Output {
201        unsafe {
202            self.array
203                .slice_unchecked(r.start().untrusted().as_usize()..r.end().untrusted().as_usize())
204        }
205    }
206}
207
208impl<'id, Array: TrustedContainer + ?Sized, I: Idx, P> ops::Index<ops::RangeFrom<Index<'id, I, P>>>
209    for Container<'id, Array>
210{
211    type Output = Array::Slice;
212
213    fn index(&self, r: ops::RangeFrom<Index<'id, I, P>>) -> &Self::Output {
214        &self[Range::from(r.start, self.end())]
215    }
216}
217
218impl<'id, Array: TrustedContainer + ?Sized, I: Idx, P> ops::Index<ops::RangeTo<Index<'id, I, P>>>
219    for Container<'id, Array>
220{
221    type Output = Array::Slice;
222
223    fn index(&self, r: ops::RangeTo<Index<'id, I, P>>) -> &Self::Output {
224        &self[Range::from(self.start(), r.end)]
225    }
226}
227
228impl<'id, Array: TrustedContainer + ?Sized> ops::Index<ops::RangeFull> for Container<'id, Array> {
229    type Output = Array::Slice;
230
231    fn index(&self, _: ops::RangeFull) -> &Self::Output {
232        &self[Range::<usize, _>::from(self.start(), self.end())]
233    }
234}
235
236impl<'id, Array: TrustedContainer + Copy> Copy for Container<'id, Array> {}
237impl<'id, Array: TrustedContainer + Clone> Clone for Container<'id, Array> {
238    fn clone(&self) -> Self {
239        unsafe { Container::new(self.array.clone()) }
240    }
241}
242
243impl<'id, Array: TrustedContainer + fmt::Debug + ?Sized> fmt::Debug for Container<'id, Array> {
244    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
245        f.debug_tuple("Container<'id>").field(&&self.array).finish()
246    }
247}