libdiffsitter/
neg_idx_vec.rs

1//! Negative index vector
2//!
3//! A Python-style negative index vector.
4
5use std::ops::{Index, IndexMut};
6
7/// A vector that can be indexed with a negative index, like with Python.
8///
9/// ```rust
10/// use libdiffsitter::neg_idx_vec::NegIdxVec;
11/// let v = NegIdxVec::from(vec![1, 2, 3]);
12/// let last_negative = v[-1];
13/// let last = v[(v.len() - 1).try_into().unwrap()];
14/// ```
15///
16/// A negative index corresponds to an offset from the end of the vector.
17#[derive(Debug, Clone, Eq, PartialEq)]
18pub struct NegIdxVec<T> {
19    /// The underlying vector for the negative index vector
20    pub data: Vec<T>,
21
22    /// An optional size constraint. Since vectors are dynamically sized, you can define the offset
23    /// up front rather than infer it from the vector's size.
24    len: usize,
25}
26
27#[allow(dead_code)]
28impl<T> NegIdxVec<T> {
29    /// Create a negative index vector with a given size.
30    ///
31    /// This will create an internal vector and all offsets will be pegged relative to the size of
32    /// this vector.
33    ///
34    /// ```rust
35    /// use libdiffsitter::neg_idx_vec::NegIdxVec;
36    /// let v: NegIdxVec<usize> = NegIdxVec::new(1, Default::default);
37    /// ```
38    pub fn new<F>(len: usize, f: F) -> Self
39    where
40        F: FnMut() -> T,
41    {
42        let mut v = Vec::new();
43        v.resize_with(len, f);
44
45        Self { data: v, len }
46    }
47
48    /// Reserve capacity for a number of *additional* elements.
49    pub fn reserve(&mut self, additional: usize) {
50        self.data.reserve(additional);
51    }
52
53    /// Reserve space for exactly `additional` elements.
54    ///
55    /// This will not over-allocate.
56    pub fn reserve_exact(&mut self, additional: usize) {
57        self.data.reserve_exact(additional);
58    }
59
60    /// Return the total number of elements the vector can hold without requiring another
61    /// allocation.
62    pub fn capacity(&self) -> usize {
63        self.data.capacity()
64    }
65
66    /// An internal helper for the indexing methods.
67    ///
68    /// This will resolve a potentially negative index to the "real" index that can be used
69    /// directly with the internal vector.
70    ///
71    /// If the index is less zero then the index will be transformed by adding `idx` to the offset
72    /// so negative indices are relative to the end of the vector.
73    fn idx_helper(&self, idx: i32) -> usize {
74        let len: i32 = self.len.try_into().unwrap();
75
76        let final_index = if idx >= 0 {
77            idx.try_into().unwrap()
78        } else {
79            let offset_idx = len + idx;
80            debug_assert!(offset_idx >= 0);
81            offset_idx.try_into().unwrap()
82        };
83        debug_assert!(final_index < len.try_into().unwrap());
84        final_index
85    }
86
87    /// Get the length of the vector
88    #[must_use]
89    pub fn len(&self) -> usize {
90        self.data.len()
91    }
92
93    /// Returns whether the vector is empty.
94    #[must_use]
95    pub fn is_empty(&self) -> bool {
96        self.data.is_empty()
97    }
98}
99
100impl<T> From<Vec<T>> for NegIdxVec<T> {
101    fn from(v: Vec<T>) -> Self {
102        // Need to capture the length before the borrow, and usize is a trivial copy type.
103        let len = v.len();
104        Self { data: v, len }
105    }
106}
107
108impl<T> FromIterator<T> for NegIdxVec<T> {
109    fn from_iter<Iter: IntoIterator<Item = T>>(iter: Iter) -> Self {
110        let data = Vec::from_iter(iter);
111        let len = data.len();
112        Self { data, len }
113    }
114}
115
116impl<T: Clone> From<&[T]> for NegIdxVec<T> {
117    fn from(value: &[T]) -> Self {
118        let v: Vec<T> = Vec::from(value);
119        let len = v.len();
120        Self { data: v, len }
121    }
122}
123
124impl<T> Default for NegIdxVec<T> {
125    fn default() -> Self {
126        Self {
127            data: Vec::new(),
128            len: 0,
129        }
130    }
131}
132
133impl<T> Index<i32> for NegIdxVec<T> {
134    type Output = T;
135
136    fn index(&self, idx: i32) -> &<Self as std::ops::Index<i32>>::Output {
137        &self.data[self.idx_helper(idx)]
138    }
139}
140
141impl<T> IndexMut<i32> for NegIdxVec<T> {
142    fn index_mut(&mut self, idx: i32) -> &mut <Self as std::ops::Index<i32>>::Output {
143        let offset_idx = self.idx_helper(idx);
144        &mut self.data[offset_idx]
145    }
146}
147
148impl<T> IntoIterator for NegIdxVec<T> {
149    type Item = T;
150    type IntoIter = std::vec::IntoIter<Self::Item>;
151
152    fn into_iter(self) -> Self::IntoIter {
153        self.data.into_iter()
154    }
155}
156
157#[cfg(test)]
158mod tests {
159    use super::*;
160    use pretty_assertions::assert_eq;
161    use rstest::rstest;
162
163    /// Generate a test vector for test cases
164    fn test_vector() -> Vec<u32> {
165        (0..100).collect()
166    }
167
168    #[test]
169    fn test_negative_indices() {
170        let vec = test_vector();
171        let neg_vec = NegIdxVec::<u32>::from(vec.clone());
172
173        for (idx, &elem) in vec.iter().rev().enumerate() {
174            assert_eq!(elem, neg_vec[-(idx as i32 + 1)]);
175        }
176    }
177
178    #[test]
179    fn test_positive_indices() {
180        let vec = test_vector();
181        let neg_vec = NegIdxVec::<u32>::from(vec.clone());
182
183        for (idx, &elem) in vec.iter().enumerate() {
184            assert_eq!(elem, neg_vec[idx as i32]);
185        }
186    }
187
188    #[test]
189    #[should_panic]
190    fn test_positive_overflow() {
191        let vec = NegIdxVec::<u32>::from(test_vector());
192        let _ = vec[vec.len() as i32 + 1];
193    }
194
195    #[test]
196    fn test_is_empty() {
197        {
198            let vec = NegIdxVec::<u8>::default();
199            assert!(vec.is_empty());
200            assert_eq!(vec.len(), 0);
201        }
202        {
203            let vec = NegIdxVec::<u8>::from(vec![0, 1, 2, 3]);
204            assert!(!vec.is_empty());
205            assert_eq!(vec.len(), 4);
206        }
207    }
208
209    #[test]
210    #[should_panic]
211    fn test_negative_overflow() {
212        let vec = NegIdxVec::<u32>::from(test_vector());
213        let idx = (vec.len() as i32) * -2;
214        let _ = vec[idx];
215    }
216
217    #[rstest]
218    #[case(1)]
219    #[case(2)]
220    #[case(10)]
221    fn test_create_new_with_size(#[case] size: usize) {
222        let vec = NegIdxVec::<u32>::new(size, Default::default);
223        assert_eq!(vec.len(), size);
224    }
225
226    #[rstest]
227    #[case(1)]
228    #[case(10)]
229    #[case(200)]
230    fn test_reserve_inexact(#[case] additional_elements: usize) {
231        let mut vec = NegIdxVec::<u8>::default();
232        assert_eq!(vec.len(), 0);
233        vec.reserve(additional_elements);
234        assert!(vec.capacity() >= additional_elements);
235    }
236
237    #[test]
238    fn test_create_default() {
239        let vec = NegIdxVec::<u8>::default();
240        assert_eq!(vec.len(), 0);
241        assert!(vec.is_empty());
242    }
243
244    #[test]
245    fn test_into_iter() {
246        let source_vec: Vec<i32> = vec![0, 1, 2, 3, 10, 49];
247        let neg_idx_vec: NegIdxVec<i32> = NegIdxVec::from(&source_vec[..]);
248        let collected_vec: Vec<i32> = neg_idx_vec.into_iter().collect();
249        assert_eq!(source_vec, collected_vec);
250    }
251
252    #[test]
253    fn test_from_iter() {
254        let source_vec: Vec<i32> = vec![0, 1, 2, 3, 10, 49];
255        let neg_idx_vec = NegIdxVec::from_iter(source_vec.clone().into_iter());
256        let extracted_vec: Vec<i32> = neg_idx_vec.into_iter().collect();
257        assert_eq!(source_vec, extracted_vec);
258    }
259}