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