Skip to main content

compressed_intvec/fixed/
slice.rs

1//! # Zero-Copy Slices
2//!
3//! This module provides [`FixedVecSlice`], a zero-copy view into a portion of a
4//! [`FixedVec`]. Slices can be created from both immutable and mutable vectors
5//! and provide a way to operate on a sub-region of the data without copying.
6//!
7//! # Examples
8//!
9//! ## Creating and using an immutable slice
10//!
11//! ```rust
12//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
13//! use compressed_intvec::fixed::{FixedVec, UFixedVec};
14//!
15//! let data: Vec<u32> = (0..10).collect();
16//! let vec: UFixedVec<u32> = FixedVec::builder().build(&data)?;
17//!
18//! // Create a slice of the elements from index 2 to 6.
19//! let slice = vec.slice(2, 5).ok_or("slice failed")?;
20//!
21//! assert_eq!(slice.len(), 5);
22//! assert_eq!(slice.get(0), Some(2));
23//! assert_eq!(slice.get(4), Some(6));
24//!
25//! // The slice can be iterated over.
26//! let sum: u32 = slice.iter().sum();
27//! assert_eq!(sum, 2 + 3 + 4 + 5 + 6);
28//! # Ok(())
29//! # }
30//! ```
31//!
32//! ## Creating and using a mutable slice
33//!
34//! ```rust
35//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
36//! use compressed_intvec::fixed::{FixedVec, UFixedVec, BitWidth};
37//!
38//! let data: Vec<u32> = (0..10).collect();
39//! let mut vec: UFixedVec<u32> = FixedVec::builder().bit_width(BitWidth::Explicit(7)).build(&data)?;
40//!
41//! // `split_at_mut` is one way to get mutable slices.
42//! let (_, mut slice2) = vec.split_at_mut(5);
43//!
44//! // Mutate an element within the second slice.
45//! if let Some(mut proxy) = slice2.at_mut(0) { // index 0 of slice2 is index 5 of vec
46//!     *proxy = 99;
47//! }
48//!
49//! assert_eq!(vec.get(5), Some(99));
50//! # Ok(())
51//! # }
52//! ```
53
54use crate::fixed::{
55    iter::FixedVecSliceIter,
56    proxy::MutProxy,
57    traits::{Storable, Word},
58    FixedVec,
59};
60use dsi_bitstream::prelude::Endianness;
61use std::ops::{Deref, DerefMut, Range};
62
63/// A zero-copy view into a contiguous portion of a [`FixedVec`].
64///
65/// A slice is a view that allows for operations on a sub-region of a [`FixedVec`]
66/// without copying the underlying data. It can be created from both immutable
67/// and mutable vectors.
68///
69/// This struct is generic over `V`, the type of reference to the parent vector,
70/// which can be `&FixedVec` (for an immutable slice) or `&mut FixedVec` (for a
71/// mutable slice).
72#[derive(Debug)]
73pub struct FixedVecSlice<V> {
74    pub(super) parent: V,
75    pub(super) range: Range<usize>,
76}
77
78// Common implementation for both immutable and mutable slices.
79impl<T, W, E, B, V> FixedVecSlice<V>
80where
81    T: Storable<W>,
82    W: Word,
83    E: Endianness,
84    B: AsRef<[W]>,
85    V: Deref<Target = FixedVec<T, W, E, B>>,
86{
87    /// Creates a new `FixedVecSlice`.
88    pub(super) fn new(parent: V, range: Range<usize>) -> Self {
89        // The range is asserted to be within the parent's bounds upon creation.
90        debug_assert!(range.end <= parent.len());
91        Self { parent, range }
92    }
93
94    /// Returns the number of elements in the slice.
95    #[inline]
96    pub fn len(&self) -> usize {
97        self.range.len()
98    }
99
100    /// Returns `true` if the slice is empty.
101    #[inline]
102    pub fn is_empty(&self) -> bool {
103        self.range.is_empty()
104    }
105
106    /// Returns the element at `index` relative to the start of the slice.
107    ///
108    /// Returns `None` if `index` is out of bounds of the slice.
109    #[inline]
110    pub fn get(&self, index: usize) -> Option<T> {
111        if index >= self.len() {
112            return None;
113        }
114        // SAFETY: The bounds check above ensures `index` is valid.
115        Some(unsafe { self.get_unchecked(index) })
116    }
117
118    /// Returns the element at `index` without bounds checking.
119    ///
120    /// The index is relative to the start of the slice.
121    ///
122    /// # Safety
123    ///
124    /// Calling this method with an out-of-bounds `index` is undefined behavior.
125    #[inline(always)]
126    pub unsafe fn get_unchecked(&self, index: usize) -> T {
127        debug_assert!(index < self.len());
128        // The index is relative to the slice, so we add the slice's start offset
129        // to get the correct index in the parent vector.
130        unsafe { self.parent.get_unchecked(self.range.start + index) }
131    }
132
133    /// Returns an iterator over the elements in the slice.
134    pub fn iter(&self) -> FixedVecSliceIter<'_, T, W, E, B, V> {
135        FixedVecSliceIter::new(self)
136    }
137
138    /// Binary searches this slice for a given element.
139    ///
140    /// If the value is found, returns `Ok(usize)` with the index of the
141    /// matching element within the slice. If the value is not found, returns
142    /// `Err(usize)` with the index where the value could be inserted to
143    /// maintain order.
144    pub fn binary_search(&self, value: &T) -> Result<usize, usize>
145    where
146        T: Ord,
147    {
148        let mut low = 0;
149        let mut high = self.len();
150
151        while low < high {
152            let mid = low + (high - low) / 2;
153            // SAFETY: The loop invariants ensure `mid` is always in bounds.
154            let mid_val = unsafe { self.get_unchecked(mid) };
155
156            match mid_val.cmp(value) {
157                std::cmp::Ordering::Less => low = mid + 1,
158                std::cmp::Ordering::Equal => return Ok(mid),
159                std::cmp::Ordering::Greater => high = mid,
160            }
161        }
162        Err(low)
163    }
164}
165
166// Mutable-only implementation.
167impl<T, W, E, B, V> FixedVecSlice<V>
168where
169    T: Storable<W>,
170    W: Word,
171    E: Endianness,
172    B: AsRef<[W]> + AsMut<[W]>,
173    V: Deref<Target = FixedVec<T, W, E, B>> + DerefMut,
174{
175    /// Returns a mutable proxy for an element at `index` within the slice.
176    ///
177    /// This allows for syntax like `*slice.at_mut(i).unwrap() = new_value;`.
178    ///
179    /// Returns `None` if the index is out of bounds of the slice.
180    pub fn at_mut(&mut self, index: usize) -> Option<MutProxy<'_, T, W, E, B>> {
181        if index >= self.len() {
182            return None;
183        }
184        // The index is translated to the parent vector's coordinate space.
185        Some(MutProxy::new(&mut self.parent, self.range.start + index))
186    }
187}
188
189// --- PartialEq Implementations ---
190
191impl<T, W, E, B, V1, V2> PartialEq<FixedVecSlice<V2>> for FixedVecSlice<V1>
192where
193    T: Storable<W> + PartialEq,
194    W: Word,
195    E: Endianness,
196    B: AsRef<[W]>,
197    V1: Deref<Target = FixedVec<T, W, E, B>>,
198    V2: Deref<Target = FixedVec<T, W, E, B>>,
199{
200    fn eq(&self, other: &FixedVecSlice<V2>) -> bool {
201        if self.len() != other.len() {
202            return false;
203        }
204        self.iter().eq(other.iter())
205    }
206}
207
208impl<T, W, E, B, V> Eq for FixedVecSlice<V>
209where
210    T: Storable<W> + Eq,
211    W: Word,
212    E: Endianness,
213    B: AsRef<[W]>,
214    V: Deref<Target = FixedVec<T, W, E, B>>,
215{
216}
217
218impl<T, W, E, B, B2, V> PartialEq<FixedVec<T, W, E, B2>> for FixedVecSlice<V>
219where
220    T: Storable<W> + PartialEq,
221    W: Word,
222    E: Endianness,
223    B: AsRef<[W]>,
224    B2: AsRef<[W]>,
225    V: Deref<Target = FixedVec<T, W, E, B>>,
226{
227    fn eq(&self, other: &FixedVec<T, W, E, B2>) -> bool {
228        if self.len() != other.len() {
229            return false;
230        }
231        self.iter().eq(other.iter())
232    }
233}
234
235impl<T, W, E, B, B2, V> PartialEq<FixedVecSlice<V>> for FixedVec<T, W, E, B>
236where
237    T: Storable<W> + PartialEq,
238    W: Word,
239    E: Endianness,
240    B: AsRef<[W]>,
241    B2: AsRef<[W]>,
242    V: Deref<Target = FixedVec<T, W, E, B2>>,
243{
244    fn eq(&self, other: &FixedVecSlice<V>) -> bool {
245        if self.len() != other.len() {
246            return false;
247        }
248        self.iter().eq(other.iter())
249    }
250}
251
252impl<T, W, E, B, T2, V> PartialEq<&[T2]> for FixedVecSlice<V>
253where
254    T: Storable<W> + PartialEq<T2>,
255    W: Word,
256    E: Endianness,
257    B: AsRef<[W]>,
258    T2: Clone,
259    V: Deref<Target = FixedVec<T, W, E, B>>,
260{
261    fn eq(&self, other: &&[T2]) -> bool {
262        if self.len() != other.len() {
263            return false;
264        }
265        self.iter().zip(other.iter()).all(|(a, b)| a == *b)
266    }
267}
268
269impl<T, W, E, B, B2, V> PartialEq<&FixedVec<T, W, E, B2>> for FixedVecSlice<V>
270where
271    T: Storable<W> + PartialEq,
272    W: Word,
273    E: Endianness,
274    B: AsRef<[W]>,
275    B2: AsRef<[W]>,
276    V: Deref<Target = FixedVec<T, W, E, B>>,
277{
278    fn eq(&self, other: &&FixedVec<T, W, E, B2>) -> bool {
279        self.eq(*other)
280    }
281}