planus/union_vectors/
union_vector.rs

1use core::{marker::PhantomData, num::NonZeroUsize};
2
3use crate::{
4    errors::{self, ErrorKind},
5    impls::array_from_buffer,
6    slice_helpers::SliceWithStartOffset,
7    TableReadUnionVector, VectorReadUnion,
8};
9
10/// A [`slice`]-like view of a union vector into a serialized flatbuffer that deserializes on demand.
11pub struct UnionVector<'buf, T: ?Sized> {
12    tags: SliceWithStartOffset<'buf>,
13    values: SliceWithStartOffset<'buf>,
14    len: usize,
15    _marker: PhantomData<&'buf T>,
16}
17
18impl<T: ?Sized> Copy for UnionVector<'_, T> {}
19impl<T: ?Sized> Clone for UnionVector<'_, T> {
20    #[inline]
21    fn clone(&self) -> Self {
22        *self
23    }
24}
25
26impl<'buf, T: ?Sized> UnionVector<'buf, T> {
27    /// Returns an empty [`UnionVector`]
28    ///
29    /// This is typically not very useful, since the vector is read-only, but
30    /// has uses for instance as a default value.
31    #[inline]
32    #[must_use]
33    pub const fn new_empty() -> UnionVector<'buf, T> {
34        Self {
35            tags: SliceWithStartOffset {
36                buffer: &[],
37                offset_from_start: 0,
38            },
39            values: SliceWithStartOffset {
40                buffer: &[],
41                offset_from_start: 0,
42            },
43            len: 0,
44            _marker: PhantomData,
45        }
46    }
47
48    /// Checks if the vector is empty.
49    #[inline]
50    #[must_use]
51    pub fn is_empty(self) -> bool {
52        self.len == 0
53    }
54
55    /// Returns the number of elements in the vector.
56    #[inline]
57    #[must_use]
58    pub fn len(self) -> usize {
59        self.len
60    }
61}
62
63impl<'buf, T: VectorReadUnion<'buf>> UnionVector<'buf, T> {
64    /// Returns the first element of the [`UnionVector`], or [`None`] if it is empty.
65    #[inline]
66    #[must_use]
67    pub fn first(self) -> Option<crate::Result<T>> {
68        self.get(0)
69    }
70
71    /// Returns the last element of the [`UnionVector`], or [`None`] if it is empty.
72    #[inline]
73    #[must_use]
74    pub fn last(self) -> Option<crate::Result<T>> {
75        self.get(self.len().checked_sub(1)?)
76    }
77
78    /// Returns an element or sub-vector depending on the type of
79    /// index.
80    ///
81    /// - If given a position, returns the element at that
82    ///   position or `None` if out of bounds.
83    /// - If given a range, returns the sub-vector corresponding to that range,
84    ///   or `None` if out of bounds.
85    #[inline]
86    #[must_use]
87    pub fn get<I>(self, index: I) -> Option<I::Output>
88    where
89        I: UnionVectorIndex<'buf, T>,
90    {
91        index.get(self)
92    }
93
94    /// Returns an element or sub-vector, without doing bounds checking.
95    ///
96    /// For a safe alternative see [`get`].
97    ///
98    /// # Safety
99    ///
100    /// Calling this method with an out-of-bounds index is *[undefined behavior]*
101    /// even if the resulting output is not used.
102    ///
103    /// [`get`]: UnionVector::get
104    /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
105    #[inline]
106    #[must_use]
107    pub unsafe fn get_unchecked<I>(self, index: I) -> I::Output
108    where
109        I: UnionVectorIndex<'buf, T>,
110    {
111        index.get_unchecked(self)
112    }
113
114    /// Returns an iterator over the vector.
115    #[inline]
116    #[must_use]
117    pub fn iter(self) -> super::Iter<'buf, T> {
118        super::Iter::new(self)
119    }
120
121    /// Returns an iterator over `chunk_size` elements of the [`UnionVector`] at a time, starting at the
122    /// beginning of the vector.
123    ///
124    /// The chunks are [`UnionVector`]s themselves and do not overlap. If `chunk_size` does not
125    /// divide the length of the [`UnionVector`], then the last chunk will not have length `chunk_size`.
126    ///
127    /// See [`chunks_exact`] for a variant of this iterator that returns chunks of always exactly
128    /// `chunk_size` elements, and [`rchunks`] for the same iterator but starting at the end of the
129    /// vector.
130    ///
131    /// # Panics
132    ///
133    /// Panics if `chunk_size` is 0.
134    ///
135    /// [`chunks_exact`]: UnionVector::chunks_exact
136    /// [`rchunks`]: UnionVector::rchunks
137    #[inline]
138    #[must_use]
139    pub fn chunks(self, chunk_size: usize) -> super::Chunks<'buf, T> {
140        let chunk_size = NonZeroUsize::new(chunk_size).expect("chunks cannot have a size of zero");
141        super::Chunks::new(self, chunk_size)
142    }
143
144    /// Returns an iterator over `chunk_size` elements of the [`UnionVector`] at a time, starting at the end
145    /// of the vector.
146    ///
147    /// The chunks are [`UnionVector`]s themselves and do not overlap. If `chunk_size` does not
148    /// divide the length of the [`UnionVector`], then the last chunk will not have length `chunk_size`.
149    ///
150    /// See [`rchunks_exact`] for a variant of this iterator that returns chunks of always exactly
151    /// `chunk_size` elements, and [`chunks`] for the same iterator but starting at the beginning
152    /// of the vector.
153    ///
154    /// # Panics
155    ///
156    /// Panics if `chunk_size` is 0.
157    ///
158    /// [`rchunks_exact`]: UnionVector::rchunks_exact
159    /// [`chunks`]: UnionVector::chunks
160    #[inline]
161    #[must_use]
162    pub fn rchunks(self, chunk_size: usize) -> super::RChunks<'buf, T> {
163        let chunk_size = NonZeroUsize::new(chunk_size).expect("chunks cannot have a size of zero");
164        super::RChunks::new(self, chunk_size)
165    }
166
167    /// Returns an iterator over `chunk_size` elements of the [`UnionVector`] at a time, starting at the
168    /// beginning of the vector.
169    ///
170    /// The chunks are [`UnionVector`]s themselves and do not overlap. If `chunk_size` does not
171    /// divide the length of the vector, then the last up to `chunk_size-1` elements will
172    /// be omitted and can be retrieved from the `remainder` function of the iterator.
173    ///
174    /// Due to each chunk having exactly `chunk_size` elements, the compiler can often optimize the
175    /// resulting code better than in the case of [`chunks`].
176    ///
177    /// See [`chunks`] for a variant of this iterator that also returns the remainder as a smaller
178    /// chunk, and [`rchunks_exact`] for the same iterator but starting at the end of the vector.
179    ///
180    /// # Panics
181    ///
182    /// Panics if `chunk_size` is 0.
183    ///
184    /// [`chunks`]: UnionVector::chunks
185    /// [`rchunks_exact`]: UnionVector::rchunks_exact
186    #[inline]
187    #[must_use]
188    pub fn chunks_exact(self, chunk_size: usize) -> super::ChunksExact<'buf, T> {
189        let chunk_size = NonZeroUsize::new(chunk_size).expect("chunks cannot have a size of zero");
190        super::ChunksExact::new(self, chunk_size)
191    }
192
193    /// Returns an iterator over `chunk_size` elements of the [`UnionVector`] at a time, starting at the
194    /// end of the vector.
195    ///
196    /// The chunks are [`UnionVector`]s themselves and do not overlap. If `chunk_size` does not
197    /// divide the length of the vector, then the last up to `chunk_size-1` elements will
198    /// be omitted and can be retrieved from the `remainder` function of the iterator.
199    ///
200    /// Due to each chunk having exactly `chunk_size` elements, the compiler can often optimize the
201    /// resulting code better than in the case of [`rchunks`].
202    ///
203    /// See [`rchunks`] for a variant of this iterator that also returns the remainder as a smaller
204    /// chunk, and [`chunks_exact`] for the same iterator but starting at the beginning of the
205    /// vector.
206    ///
207    /// # Panics
208    ///
209    /// Panics if `chunk_size` is 0.
210    ///
211    /// [`rchunks`]: UnionVector::rchunks
212    /// [`chunks_exact`]: UnionVector::chunks_exact
213    #[inline]
214    #[must_use]
215    pub fn rchunks_exact(self, chunk_size: usize) -> super::RChunksExact<'buf, T> {
216        let chunk_size = NonZeroUsize::new(chunk_size).expect("chunks cannot have a size of zero");
217        super::RChunksExact::new(self, chunk_size)
218    }
219
220    /// Returns an iterator over all contiguous windows of length
221    /// `size`. The windows overlap. If the vector is shorter than
222    /// `size`, the iterator returns no values.
223    ///
224    /// # Panics
225    ///
226    /// Panics if `size` is 0.
227    #[inline]
228    #[must_use]
229    pub fn windows(self, size: usize) -> super::Windows<'buf, T> {
230        let size = NonZeroUsize::new(size).expect("windows cannot have a size of zero");
231        super::Windows::new(self, size)
232    }
233
234    /// Returns the first and all the rest of the elements of the `Vector`, or `None` if it is empty
235    #[inline]
236    #[must_use]
237    pub fn split_first(self) -> Option<(crate::Result<T>, UnionVector<'buf, T>)> {
238        if self.is_empty() {
239            None
240        } else {
241            Some(unsafe { (self.get_unchecked(0), self.get_unchecked(1..)) })
242        }
243    }
244
245    /// Returns the last and all the rest of the elements of the `Vector`, or `None` if it is empty
246    #[inline]
247    #[must_use]
248    pub fn split_last(self) -> Option<(crate::Result<T>, UnionVector<'buf, T>)> {
249        if self.is_empty() {
250            None
251        } else {
252            Some(unsafe {
253                (
254                    self.get_unchecked(self.len - 1),
255                    self.get_unchecked(..self.len - 1),
256                )
257            })
258        }
259    }
260
261    /// Divides one `Vector` into two at an index.
262    ///
263    /// The first will contain all indices from `[0, mid)` (excluding
264    /// the index `mid` itself) and the second will contain all
265    /// indices from `[mid, len)` (excluding the index `len` itself).
266    #[inline]
267    #[must_use]
268    pub fn split_at(self, mid: usize) -> Option<(UnionVector<'buf, T>, UnionVector<'buf, T>)> {
269        if mid <= self.len {
270            Some(unsafe { self.split_at_unchecked(mid) })
271        } else {
272            None
273        }
274    }
275
276    /// Divides one [`UnionVector`] into two at an index, without doing bounds checking.
277    ///
278    /// The first will contain all indices from `[0, mid)` (excluding
279    /// the index `mid` itself) and the second will contain all
280    /// indices from `[mid, len)` (excluding the index `len` itself).
281    ///
282    /// For a safe alternative see [`split_at`].
283    ///
284    /// # Safety
285    ///
286    /// Calling this method with an out-of-bounds index is *[undefined behavior]*
287    /// even if the resulting output is not used. The caller has to ensure that
288    /// `0 <= mid <= self.len()`.
289    ///
290    /// [`split_at`]: UnionVector::split_at
291    /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
292    #[inline]
293    #[must_use]
294    pub unsafe fn split_at_unchecked(
295        self,
296        mid: usize,
297    ) -> (UnionVector<'buf, T>, UnionVector<'buf, T>) {
298        (self.get_unchecked(..mid), self.get_unchecked(mid..))
299    }
300}
301
302impl<'buf, T: VectorReadUnion<'buf>> UnionVector<'buf, T> {
303    /// Copies self into a new `Vec`.
304    pub fn to_vec<O>(self) -> crate::Result<alloc::vec::Vec<O>>
305    where
306        O: core::convert::TryFrom<T>,
307        crate::errors::Error: From<O::Error>,
308    {
309        self.iter().map(|v| Ok(O::try_from(v?)?)).collect()
310    }
311}
312
313impl<'buf, T: VectorReadUnion<'buf>> IntoIterator for UnionVector<'buf, T> {
314    type Item = crate::Result<T>;
315    type IntoIter = super::Iter<'buf, T>;
316
317    #[inline]
318    fn into_iter(self) -> Self::IntoIter {
319        self.iter()
320    }
321}
322
323impl<'buf, T: VectorReadUnion<'buf> + core::fmt::Debug> core::fmt::Debug for UnionVector<'buf, T>
324where
325    T: core::fmt::Debug,
326{
327    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
328        f.debug_list().entries(*self).finish()
329    }
330}
331
332/// A helper trait used for indexing operations.
333pub trait UnionVectorIndex<'buf, T: VectorReadUnion<'buf>>: private::Sealed {
334    /// The output type returned by methods.
335    type Output;
336
337    /// Returns a the output at this location, if in bounds.
338    fn get(self, vector: UnionVector<'buf, T>) -> Option<Self::Output>;
339
340    /// Returns a mutable reference to the output at this location, without
341    /// performing any bounds checking.
342    /// Calling this method with an out-of-bounds index is
343    /// *[undefined behavior]* even if the resulting output is not used.
344    ///
345    /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
346    unsafe fn get_unchecked(self, vector: UnionVector<'buf, T>) -> Self::Output;
347}
348
349/// Convert pair of `ops::Bound`s into `core::ops::Range` without performing any bounds checking and (in debug) overflow checking
350fn into_range_unchecked(
351    len: usize,
352    (start, end): (core::ops::Bound<usize>, core::ops::Bound<usize>),
353) -> core::ops::Range<usize> {
354    use core::ops::Bound;
355    let start = match start {
356        Bound::Included(i) => i,
357        Bound::Excluded(i) => i + 1,
358        Bound::Unbounded => 0,
359    };
360    let end = match end {
361        Bound::Included(i) => i + 1,
362        Bound::Excluded(i) => i,
363        Bound::Unbounded => len,
364    };
365    start..end
366}
367
368/// Convert pair of `core::ops::Bound`s into `core::ops::Range`.
369/// Returns `None` on overflowing indices.
370fn into_range(
371    len: usize,
372    (start, end): (core::ops::Bound<usize>, core::ops::Bound<usize>),
373) -> Option<core::ops::Range<usize>> {
374    use core::ops::Bound;
375    let start = match start {
376        Bound::Included(start) => start,
377        Bound::Excluded(start) => start.checked_add(1)?,
378        Bound::Unbounded => 0,
379    };
380
381    let end = match end {
382        Bound::Included(end) => end.checked_add(1)?,
383        Bound::Excluded(end) => end,
384        Bound::Unbounded => len,
385    };
386
387    // Don't bother with checking `start < end` and `end <= len`
388    // since these checks are handled by `Range` impls
389
390    Some(start..end)
391}
392impl<'buf, T: VectorReadUnion<'buf>> UnionVectorIndex<'buf, T>
393    for (core::ops::Bound<usize>, core::ops::Bound<usize>)
394{
395    type Output = UnionVector<'buf, T>;
396
397    #[inline]
398    fn get(self, vector: UnionVector<'buf, T>) -> Option<Self::Output> {
399        into_range(vector.len, self)?.get(vector)
400    }
401
402    #[inline]
403    unsafe fn get_unchecked(self, vector: UnionVector<'buf, T>) -> Self::Output {
404        into_range_unchecked(vector.len, self).get_unchecked(vector)
405    }
406}
407
408impl<'buf, T: VectorReadUnion<'buf>> UnionVectorIndex<'buf, T> for usize {
409    type Output = crate::Result<T>;
410
411    #[inline]
412    fn get(self, vector: UnionVector<'buf, T>) -> Option<Self::Output> {
413        if self < vector.len {
414            Some(unsafe { self.get_unchecked(vector) })
415        } else {
416            None
417        }
418    }
419
420    #[inline]
421    unsafe fn get_unchecked(self, vector: UnionVector<'buf, T>) -> Self::Output {
422        debug_assert!(self < vector.len);
423        debug_assert!(vector.len.checked_mul(4).unwrap() <= vector.tags.len());
424        debug_assert!(vector.len.checked_mul(4).unwrap() <= vector.values.len());
425        let tag = *vector.tags.buffer.get_unchecked(self);
426        <T as VectorReadUnion>::from_buffer(vector.values, tag, 4 * self)
427    }
428}
429
430impl<'buf, T: VectorReadUnion<'buf>> UnionVectorIndex<'buf, T> for core::ops::Range<usize> {
431    type Output = UnionVector<'buf, T>;
432
433    #[inline]
434    fn get(self, vector: UnionVector<'buf, T>) -> Option<Self::Output> {
435        if self.start > self.end || self.end > vector.len {
436            None
437        } else {
438            // SAFETY: `self` is checked to be valid and in bounds above.
439            unsafe { Some(self.get_unchecked(vector)) }
440        }
441    }
442
443    #[inline]
444    unsafe fn get_unchecked(self, vector: UnionVector<'buf, T>) -> Self::Output {
445        debug_assert!(self.start <= self.end);
446        debug_assert!(self.end <= vector.len);
447        UnionVector {
448            tags: vector
449                .tags
450                .advance(self.start)
451                .expect("IMPOSSIBLE: the length was checked on creation"),
452            values: vector
453                .values
454                .advance(4 * self.start)
455                .expect("IMPOSSIBLE: the length was checked on creation"),
456            len: self.end - self.start,
457            _marker: PhantomData,
458        }
459    }
460}
461
462impl<'buf, T: VectorReadUnion<'buf>> UnionVectorIndex<'buf, T> for core::ops::RangeFrom<usize> {
463    type Output = UnionVector<'buf, T>;
464
465    #[inline]
466    fn get(self, vector: UnionVector<'buf, T>) -> Option<Self::Output> {
467        (self.start..vector.len).get(vector)
468    }
469
470    #[inline]
471    unsafe fn get_unchecked(self, vector: UnionVector<'buf, T>) -> Self::Output {
472        (self.start..vector.len).get_unchecked(vector)
473    }
474}
475
476impl<'buf, T: VectorReadUnion<'buf>> UnionVectorIndex<'buf, T> for core::ops::RangeFull {
477    type Output = UnionVector<'buf, T>;
478
479    #[inline]
480    fn get(self, vector: UnionVector<'buf, T>) -> Option<Self::Output> {
481        Some(vector)
482    }
483
484    #[inline]
485    unsafe fn get_unchecked(self, vector: UnionVector<'buf, T>) -> Self::Output {
486        vector
487    }
488}
489
490impl<'buf, T: VectorReadUnion<'buf>> UnionVectorIndex<'buf, T>
491    for core::ops::RangeInclusive<usize>
492{
493    type Output = UnionVector<'buf, T>;
494
495    #[inline]
496    fn get(self, vector: UnionVector<'buf, T>) -> Option<Self::Output> {
497        (*self.start()..self.end().checked_add(1)?).get(vector)
498    }
499
500    #[inline]
501    unsafe fn get_unchecked(self, vector: UnionVector<'buf, T>) -> Self::Output {
502        (*self.start()..self.end() + 1).get_unchecked(vector)
503    }
504}
505
506impl<'buf, T: VectorReadUnion<'buf>> UnionVectorIndex<'buf, T> for core::ops::RangeTo<usize> {
507    type Output = UnionVector<'buf, T>;
508
509    #[inline]
510    fn get(self, vector: UnionVector<'buf, T>) -> Option<Self::Output> {
511        (0..self.end).get(vector)
512    }
513
514    #[inline]
515    unsafe fn get_unchecked(self, vector: UnionVector<'buf, T>) -> Self::Output {
516        (0..self.end).get_unchecked(vector)
517    }
518}
519
520impl<'buf, T: VectorReadUnion<'buf>> UnionVectorIndex<'buf, T>
521    for core::ops::RangeToInclusive<usize>
522{
523    type Output = UnionVector<'buf, T>;
524
525    #[inline]
526    fn get(self, vector: UnionVector<'buf, T>) -> Option<Self::Output> {
527        (0..=self.end).get(vector)
528    }
529
530    #[inline]
531    unsafe fn get_unchecked(self, vector: UnionVector<'buf, T>) -> Self::Output {
532        (0..=self.end).get_unchecked(vector)
533    }
534}
535
536impl<'buf, T: VectorReadUnion<'buf>, O> TryFrom<UnionVector<'buf, T>> for alloc::vec::Vec<O>
537where
538    O: core::convert::TryFrom<T>,
539    errors::Error: From<O::Error>,
540{
541    type Error = crate::errors::Error;
542
543    fn try_from(value: UnionVector<'buf, T>) -> Result<Self, Self::Error> {
544        value.iter().map(|v| Ok(O::try_from(v?)?)).collect()
545    }
546}
547
548impl<'buf, T: VectorReadUnion<'buf>> TableReadUnionVector<'buf> for UnionVector<'buf, T> {
549    fn from_buffer(
550        buffer: SliceWithStartOffset<'buf>,
551        tag_offset: usize,
552        values_offset: usize,
553    ) -> core::result::Result<Self, ErrorKind> {
554        let (tags_buffer, tags_len) = array_from_buffer(buffer, tag_offset)?;
555        let (values_buffer, values_len) = array_from_buffer(buffer, values_offset)?;
556        if tags_len > tags_buffer.len() {
557            return Err(ErrorKind::InvalidLength);
558        }
559        if values_len.checked_mul(4).ok_or(ErrorKind::InvalidLength)? > values_buffer.len() {
560            return Err(ErrorKind::InvalidLength);
561        }
562        if tags_len != values_len {
563            return Err(ErrorKind::UnionVectorLengthsMismatched {
564                tags_len,
565                values_len,
566            });
567        }
568        Ok(UnionVector {
569            tags: tags_buffer,
570            values: values_buffer,
571            len: tags_len,
572            _marker: PhantomData,
573        })
574    }
575}
576
577mod private {
578    pub trait Sealed {}
579
580    // Implement for those same types, but no others.
581    impl Sealed for (core::ops::Bound<usize>, core::ops::Bound<usize>) {}
582    impl Sealed for usize {}
583    impl Sealed for core::ops::Range<usize> {}
584    impl Sealed for core::ops::RangeFrom<usize> {}
585    impl Sealed for core::ops::RangeFull {}
586    impl Sealed for core::ops::RangeInclusive<usize> {}
587    impl Sealed for core::ops::RangeTo<usize> {}
588    impl Sealed for core::ops::RangeToInclusive<usize> {}
589}